Azure app Service authentication token not refreshing after calling .\auth\refresh endpoint - azure-active-directory

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

Related

Is Refresh Token relevant for OIDC IdentityServer Azure AD SSO Implementation?

I have an implementation of IdentityServer4 which connects with Azure AD for authentication (OIDC). In the callback method, using the IdentityServertools, I am generating the access_token and redirecting the user to SPA with the same. The SPA then stores the access_token into localstorage and uses it for authentication.
Normally, when my SPA app hits the token endpoint of the IdentityServer4, it gives access_token and refresh_token and then uses refresh_token to re-authenticate a returning user.
In this case of SSO with Azure AD, do I need to generate refresh_token manually? If yes, I can build on top of default implementation and that's not the problem (However, the docs suggest against of changing the IRefreshTokenService implementation or building something from scratch)
My real question is, is there a need of refresh_token here? Because refresh_tokens are stored in DB and never get's deleted and after sometime, these refresh_tokens table will swell (right now it already has 80k rows). The user is expected to click on a small tile inside SAP's Successfactor - that will open the signin/consent screen of Azure or will directly take the user to the main page where zhe will just answer a question and done. So it's hardly 2-3 mins business. So I can continue to generate access_tokens from my IdentityServer4 for every click as I don't expect the user to stay authenticated in the browser if zhe has logged out from SAP's Successfactor (or any other app linked with Azure).
Please advise, if I should generate refresh_token? Is it a good architecture?
Access token is used to prove the request is allowed to access the resource(such as api from ms or your custom api) and refresh token is used to refresh access token to make sure the access token isn't expired. Access token will expire in an hour by default and refresh token has 90 days.
At this point, we can easily find the refresh token is designed for some special scenarios because the expired time for refresh token is much longer than access token's expired time, but we can also generate a new access token in other way such as using msal or sign in again.
As you said in the question, you can generate an access token by one click and you don't expect users to stay authenticated for a long time. So I think it's unnecessary for you to use refresh token.

How can you implement refresh tokens in a web app - angularjs

I am using token based security in my web app. The server side is wrote using c# and i am using openiddict for logging in and issuing tokens, found here. I am currencyly using Implict flow.
By default my tokens have a lifespan of 1 hour, after that you have to logging again. I have locked down my API to accept bearer tokens only and not cookies.
I wanted to implement refresh tokens but after reading many websites, it appears that implementing refresh tokens on a web app, is not a good way to go due to a hacker getting the refresh token. I know that to use refresh tokens, you must use code flow, instead of implict, which i can do.
How do people get round this situation in their web apps? I cant be the only one who wants a token to last longer than an hour in a web app?
The approach recommended by OpenID Connect is to send an authorization request in a hidden frame with the same parameters as the ones you use for the initial implicit flow request plus prompt=none and optionally, an id_token_hint corresponding to the id_token you extracted from the authorization response.
When using prompt=none, the identity provider won't display any consent form and will directly redirect the user agent to the redirect_uri you specify, with the new token appended to the URI fragment, just like for a classic implicit flow request. You can retrieve it by extracting it from the popup.location.hash property.
If the request cannot be processed (invalid request, unauthenticated user, invalid id_token_hint, consent required, etc.), an error is returned and the identity provider either redirects the user agent to the redirect_uri with an error parameter or stops processing the request.
Note that due to the same origin policy, you can't access popup.location.hash if the current location belongs to a different domain (e.g if the identity provider refuses to redirect the user agent to your client app): it will throw an access denied exception. In this case, it's always better to add a timeout to your "refresh" operation.
Sadly, there are very few libraries that can help you with this task. oidc-token-manager is one of them, but it has a few limitations that will prevent it from working OTB with OpenIddict: it doesn't support raw RSA keys (you have to explicitly use a X509 certificate in the OpenIddict options) and it doesn't send the id_token_hint parameter required by OpenIddict when sending a prompt=none request.

Google OAuth2 flow and id_token refresh

I am having troubles in implementing OAuth in the right way.
I use a client/API architecture (Angular for front and Node.js for back) and I would like user to sign in using Google OAuth authentication only.
Here is what I think is the right way for the moment (tell me if I misunderstood something) :
Angular open a Google popup asking user's consent.
Once the user agree, Google Authorization server sends back to angular a verification code.
This verification code is forwarded to an API endpoint.
Then, the API asks Google Authorization server to exchange this code for an access_token, an id_token and a refresh_token.
Google sends those 3 tokens.
The API uses access_token to retrieve user from Google API
The API persists the user
Here is the little dillema, in my opinion, the access_token and refresh_token should be stored into the database and the id_token should be sent back to Angular client.
This way, it would allow the API to ask for resource in Google API and if the token expires it can still ask for a new token thanks to the refresh_token.
Client-side, the id_token is embedded in all requests thus allowing the API to identify the client and verify his authentication with Google certs from https://www.googleapis.com/oauth2/v3/certs.
Supposing this is right way to use tokens, how could I deal with id_token expiration since client does not have any refresh token ?
Thanks !
I do it slightly different (I have the same basic architecture though).
Angular decides the user needs to log in and displays a login popup.
The url in the login popup is not serviced by angular, but is instead directly run off of the backend server: /auth/google . (I use hapijs and bell, personally).
/auth/google is serviced by a bell plugin and initiates the OAUTH dance.
the end of the OAUTH dance results in my node server generating a local token (I just generate random bytes and store them in redis mapped to user ids)
because the initial login popup was created by window.open, the success page (generated on the api side rather than in angular) can use window.opener.postMessage to communicate the token back to the angular runtime.
This way, all my sensitive google credentials (the user's oauth token, refresh token if needed, and my application's api ID and secret) are only on the server, except for during the OAUTH dance relay when they're in a URL string during the client redirects. This is reasonably secure.
Then for all the actual user interactions with the api, I use the token I generated in step four to authenticate. This could be a JWT if you wanted, but I don't do it that way; I just use redis to map from 'longrandostring' -> userId. That lets me (for example) force everyone to re-login if I wipe the redis database that has all the tokens stored, or I can write a lua script to delete all the entries that map to a certain userid.
If you need a refresh token, you can set access_type=offline in the initial request to oauth2/auth, and you'll get a refresh token as part of the response, unless you've previously gotten a refresh token. You can then persist it on the server side and get new access tokens as needed. If you set approval_prompt=force as well, you'll force a new consent screen and be guaranteed a refresh token (but after some small number of refresh tokens granted to a user, older ones expire on the same application so it's best to only request them if really needed).

OAuth 2.0 with GMAIL API using

I am trying to create a sample program for Install Application to access google mail api using oAuthentication. But I have a query before start working on it.
I read on most of doc and following this link https://developers.google.com/identity/protocols/OAuth2InstalledApp
First we need to request for token by providing ClientID and SecretKey
That will return Token in response that Token is pass to other google service which we want to access.
During this process code open the browser (Single time) to get token id. It can not happen without browser.
Can't we do http request to google then get respond from it without using browser, like access any rest api we do?
From Google's Using OAuth 2.0 to Access Google APIs:
The authorization sequence begins when your application redirects a
browser to a Google URL; the URL includes query parameters that
indicate the type of access being requested. Google handles the user
authentication, session selection, and user consent. The result is an
authorization code, which the application can exchange for an access
token and a refresh token.
The application should store the refresh token for future use and use
the access token to access a Google API. Once the access token
expires, the application uses the refresh token to obtain a new one.
You have no choice but to redirect the user to Google the first time the user is using your application. However, you can ask for a refresh token that you can use to refresh the access token after this first login, which usually expires after one hour.

when using mirror api, how do i handle access token expiration

I am building a glass app using the PHP sample app as reference.
To my understanding, the glass app is a web app which is user facing. When the user visit the web app, they will authorize the web app (using oauth2) to access their resources, and once authorization succeeded the web app will get an access token, which is then saved in a sqlite database (there's this store_credentials() function which stores the access token).
So if I have 100 users who visit the web app and register, the database will hold 100 access tokens for these users. Let's say I have some backend code which pull from a RSS feed every hour, and whenever I find there's a new story I will push it to all registered users. Let's say I have a cron job which does this, when when this job is triggered, I will find all the access tokens in the database and use them to make mirror API calls to insert cards. But what if some of the access token is expired when I am trying to make the mirror API call? It seems I will need to ask user to re-authorized, but at this point I am not interacting with the user. If I have a refresh token, I may be able to call oauth again to get a new access token.
Thanks.
You pretty much answered your own question - when you request permission using OAuth, you should also request "offline" access. The first time you do this for each user it will give you a refresh token along with your access token, and you should store both. If you did not request offline access initially, you will need to revoke the tokens that have been granted and re-grant them with offline access.
If the sample app you're referring to is the one at https://github.com/googleglass/mirror-quickstart-php, all this should be done for you already with the libraries included. The credentials returned from $client->getAccessToken() in oauth2callback.php should include both the access and refresh tokens, and these are saved in that same file by calling store_credentials(). The client libraries should check if the access token has expired and, if so, it gets a new one with the refresh token before making the call.
You may want to take a look at the contents of the credentials object at these various points and make sure there is a refresh token. If not, try revoking all the tokens and starting again, since it sounds like the first time you authorized the client you did so without requesting offline access.
If you are doing this yourself, best practice is to either refresh the access token if it has expired or is about to expire (since there may be some delay) before use, or (even better) attempt to make the call with an access token and, if it fails with an authentication error, get a new access token via the refresh token and try again.

Resources