What is difference between AcquireTokenForClient vs AcquireTokenSilent In MSAL.NET? - azure-active-directory

I have started using MSAL.
now I have 2 implementation of getting token from MSAL #
##First one is##
app.AcquireTokenForClient(scopes).ExecuteAsync();
## another one is ##
app.AcquireTokenSilent
what is difference between them ?

AcquireTokenSilent() is for user based authentication and AcquireTokenForClient() is for app-only authentication (used in service to service calls, for example).
The AcquireTokenSilent needs an account parameter when called, so it can search the user token cache for a valid accessToken.
The AcquireTokenForClient only needs a scope parameter - there is no user context involved, the accessToken is acquired on behalf of the application itself.

Related

Changing AAD accessTokenAcceptedVersion does not result in token version change

I created a new app registration for an app service resource and the accessTokenAcceptedVersion was set to 2 by default (or may be due to my choices). Some clients got an access token for this resource and is being potentially cached on that client.
Later I changed the accessTokenAcceptedVersion to null (i.e. default 1). Now those clients fail to authenticate to the resource (rightfully so) given they have a cached v2 token.
The client in this case is an azure function using the AzureServiceTokenProvider library for getting & caching tokens. So I tried various ways to invalidate the cache involving :
Restarting the function
Waiting out for 1 day (the token expiration time)
Making changes to the app registration
Unfortunately Revoke-AzureADUserAllRefreshToken doesn't seem to work for managed identities.
But I'm still getting a V2 token. What can be done to invalidate this cache or to force a V1 token somehow? I'm trying to do this without any code change currently. But can deploy a change if needed at all.
On other machines/infra I'm able to get a V1 token for the same resource using the different identity. As per my understanding, the token version purely only depends on accessTokenAcceptedVersion. Nothing else.
Please check below points:
If you have changed the accessTokenAcceptedVersion to ‘null’ value, it may also permit v2.0 tokens – It is an issue on AAD’s side. It also depends on the way the app is registered. The difference is that:
If it was done using Azure Portal, then the 'accessTokenAcceptedVersion' field of manifest is set to 'null'
If it was done in the App Registration portal(https://apps.dev.microsoft.com), then it may point to version ‘2.0’
Also check :
The v1 authorization endpoint : https://login.microsoftonline.com/tenantid/oauth2/authorize?
The v1 token endpoint : https://login.microsoftonline.com/tenantid/oauth2/token
Try to change the authority and instance to v1 endpoint in the code and also wherever it is used in the code.
See Microsoft identity platform access tokens - Microsoft identity platform | Microsoft Docs
Both v1 and v2.0 endpoints have their own parameters separately. The v2.0 endpoint expect the ‘scope’ parameter in the request, whereas v1 endpoint expect ‘resource’. So do check the scopes for the api you are referring to and change the code accordingly.
If you still get v2.0 tokens, you must clear the cache of the client application calling your API otherwise until the token has expired, token would be taken from the cache, and would still be a v2.0 token. For that, you may try to call AcquireTokenSilentAsync (in the client while calling you Web API) to force the refresh.
Reference:
Wrong version of access token (got Azure AD V1 instead of V2) GitHub

Azure app Service authentication token not refreshing after calling .\auth\refresh endpoint

I have an angular app hosted in one azure windows app service and a asp.net core api hosted in another, both are protected using the app service authentication feature. I am using an app service plan which currently has two servers. I am using the token store and it is using the file system to store the tokens.
When the angular app calls the api it needs to pass an access token in the authorization header. The access token is retrieved by performing a GET on the \.auth\me endpoint and sending the AppServiceAuthSession cookie as the credential. The returned token is then cached in session storage and used for subsequent requests until the token expires. When the token expires I call the \.auth\refresh endpoint (and send the AppServiceAuthSession cookie) and then call the \.auth\me to get the refreshed token.
All this works well when running on one server but when the app service plan is scaled to 2 or more servers the call to \.auth\refresh succeeds but the subsequent call to the .auth\me endpoint gets a token which has already expired. I can tell the token has expired by checking the internal exp claim and also the call to the api fails with a 401 Unauthorized when it would normally succeed.
If I scale back to one server the problem goes away and if I enable ARR affinity the problem goes away but I don't want to have to enable affinity just to resolve this.
also the call to the api fails with a 401 Unauthorized when it would normally succeed.
You shouldn't be calling /.auth/refresh on the API app. You must call /.auth/refresh on the WEB app because the auth token is produced on the WEB app.
Alternative to the bearer token is the AppServiceAuthSession cookie from your client request to /.auth/refresh.
The returned token is then cached in session storage and used for subsequent requests until the token expires. When the token expires I call the .auth\refresh endpoint (and send the AppServiceAuthSession cookie) and then call the .auth\me to get the refreshed token.
We have a user-facing web application that calls an API backend, which in turn queries the MSFT graph, just to be sure we have the context correctly captured. At each phase, we anticipate a distinct AAD registration:
Web app registration (with permission to API app)
API app registration (with permission to Microsoft Graph)
Our initial assumption is that the user hasn't explicitly given their agreement to using the Microsoft Graph because this looked to be tied to adding the graph permission. Since the user isn't involved if you're merely updating the existing token, there isn't a chance for that consent to be obtained. The user would need to re-login with an explicit consent prompt, which could be done by calling /.auth/login/aad?prompt=consent. But we wouldn't anticipate that to cause the expiration problem. I'm speculating here, but it's possible that a cached, outdated value is being utilised in place of the permission error. There would need to be input from Connor.
The logs from the actual Authentication / Authorization layer itself, particularly if there is an error message, are another piece of information that would be useful to have. To obtain these logs:
From your app in the portal (the API app is what I'm most interested in): “Platform features” > “Diagnostic logs”. Change “Application Logging (Filesystem)” to “On” and set the level to “verbose.” Hit Save.
Back to “Platform features” > “Log stream” and keep that window open as you reproduce the issue (in this case, the call to the API app)
Actually, the issue is that AAD bearer tokens are incompatible with the /.auth/refresh API. Instead, you must use a session token (_x-zumo-auth_ header) or a session cookie (_AppServiceAuthSession_ cookie), which are the results of a login activity.
For more info - see here #refreshing-the-access-tokens

IdentityServer4 - Understanding flows and Endpoints. How is it related to OAuth and OpenIDConnect?

I am integrating the security aspect of webapplication. I have decided to use OAuth,
so we have a REST WebApi in AspNet Core 3.0, the client which is a SPA created in React, and the Identity Server 4.0 app which is also in AspNet Core 3.0.
I read that OAuth is created for Authorization and not for Authentication.
For Authentication, seems that exists something else called OpenIDConnect, so the first question that comes to my mind, and on which I cannot find an easy answer is: are OAuth, OpenIDConnect and IdentityServer related technology?
Which is the best solution for authentication, considering that I would like to create users in a SqlServer Database, and if it's possible I would like to use Entity Framework for the porpose?
The flow for my authentication would be:
User writes Username and Password, if they are right he receive the JWT Token, without redirecting him/her to the authorization page.
At this point the problem are:
which is the right endpoint to do this flow:
is it the /authorize or the /token endpoint?
I have a lot of confusion for the questions above.
The second thing, what is the best way to retrieve the user informations?
For example if my endpoint needs to understand from the logged in user what are his data, I think that or I retrieve from the endpoint or from the JWT token.
Even here I have no clue on which is the best.
I read that OAuth is created for Authorization and not for Authentication. For Authentication, seems that exists something else called OpenIDConnect, so the first question that comes to my mind, and on which I cannot find an easy answer is: are OAuth, OpenIDConnect and IdentityServer related technology?
That's right. OAuth was the first one introduced and allows the person requesting it access to the resources (its handing out access tokens). OIDC (OpenID Connect) on the other-side extends this concept by an identity, the authentication part.
The identity token verifies the identity of the person to your application. Instead of providing identity via username + password (i.e. user creating an account on your website), they get redirected to your authentication provider/app and enter their login there and you get an identity token in return (and/or an access token, depending on the flow and scopes you request).
The identity token is an JWT token (or reference token). The JWT token contains all of the users identity information required for your application (user id, email, displayname, age, etc.) and is cryptographically signed. Only the Identity Server knows the key used to sign it up and you can verify it with the public key from the OIDC (IdSrv here) provider.
Reference token works similar, but claims are requested on the server side and cached.
With identity token you can not access the users resources. Example: Facebook.
When you sign in your application with an facebook account, most page will only request identity token to verify that its the same user (instead of using a username / password combination). But with that one, the application can't access your facebook posts or do posts in your name.
If the application requests an access token (token scope), then also an access token will be returned (if the application is allowed to via allowed scopes). You will be asked to grant the permissions to the resources which the application requests.
With that token, the application can read your posts or post in your name.
Which is the best solution for authentication, considering that I would like to create users in a SqlServer Database, and if it's possible I would like to use Entity Framework for the porpose?
Doesn't really matter. Either one can be used, all you really need is the "sid" (subject id) claim and associate that one with your user.
Identity Server can issue both, depending on what the client asks (if client asks for id_token response type, it will receive an identity token, if it asks for token an access token. Both can be specified or just one).
At this point the problem are: which is the right endpoint to do this flow: is it the /authorize or the /token endpoint? I have a lot of confusion for the questions above.
/authorize is used to authorize the user (have him login, and send back to your website). Its used for so called interactive flows, where the user enters credentials
/token endpoint you can only retrieve a token (resource owner flow (username + password), client credentials (for machine to machine authentication), refresh token (to get a new access token by using an refresh token (if you asked for offline_access scope, which gives and refresh token)
The second thing, what is the best way to retrieve the user informations?
the /userinfo endpoint, see docs: http://docs.identityserver.io/en/latest/endpoints/userinfo.html
As the doc says to access that, the client needs to request the openid scope.
For example if my endpoint needs to understand from the logged in user what are his data, I think that or I retrieve from the endpoint or from the JWT token.
Yes you can retrieve it from JWT token, if you use JWT token. If you use reference token, its just an ID.
And last but not least the /introspection endpoint can be used to validate the token (if your consuming application has no libraries to decrypt and validate signature of the token.
If you can, its best to use the Identity Server client libraries (i.e. IdentityServer4.AccessTokenValidation package for ASP.NET Core or oidc-client for npm/javascript based applications) which should be picking up the correct endpoints, so you don't have to worry about it

ID tokens vs Access tokens in implicit grant flow

I would like some clarification on how to use ID tokens and access tokens in an implicit grant flow.
I have an Angular SPA using MSAL.js for Angular package and a Web API. The API does NOT call any external services, like MSFT Graph. The back end uses role claims from JWT to establish RBAC authorization to our API functionality only.
This doc says:
https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens
id_tokens are sent to the client application as part of an OpenID Connect flow. They can be sent along side or instead of an access token, and are used by the client to authenticate the user.
ID Tokens should be used to validate that a user is who they claim to be and get additional useful information about them - it shouldn't be used for authorization in place of an access token.
This doc shows an authentication flow where a web API is called with an ID token instead of an access token:
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow
This sample code sends ID token too (calling own Web API sample):
https://github.com/Azure-Samples/active-directory-javascript-singlepageapp-dotnet-webapi-v2
The sample back end validates ID token audience against Client ID of the app that issued it.
Given our API is the only consumer of tokens, should we use ID tokens or access tokens?
Generally speaking, when securing your API with the Microsoft Identity platform, clients should be using the access token when making API requests, not the ID token.
check this part
It first calls acquireTokenSlient, which gets a token from the cache if available if not
it calls acquireTokenPopUp which will get an acces token for the specific scope, I am not sure if it would open up a popup window or will get a token in the background with hidden iframe. But it would fetch an access token for sure. API can never be accessed with ID token.
check https://learn.microsoft.com/bs-latn-ba/azure/active-directory/develop/scenario-spa-acquire-token for more clarification

Identity server 4 and Angular app authenticate to achieve forever lasting session

Current structure:
I have angular app as Client, API as data source and Identity server as Identity source. In angular I authenticate user against identity server and use returned token to authorize user against API. Which Grant type should I use to authenticate my angular app user against identity server?
According to http://docs.identityserver.io/en/release/topics/grant_types.html, I should use Implicit grant type.
Problem is that with Implicit grant type there are no refresh tokens. So my question is, how should I keep my user session alive in angular app (access token valid) ? - without asking user to authenticate himself after every time access token gets expired.
My goal is to have session alive (token valid) until its killed by user (logged out)
Yes, the Implicit flow is the one I would use. If your authentication provider supports sessions, you can use OpenID Connect to get new tokens without user interaction. Before the token expiration, create an authentication request (/auth endpoint) in a hidden iframe with prompt=none URL parameter (id_token_hint=... parameter should be present too according to the OpenID Connect RFC). If the user session is open, you will get new tokens, otherwise error is returned (user interaction required).
There is the OpenID Connect Session Management RFC that describes this process and more (single sign on).

Resources