Azure AD Easy Auth headers are empty after expiry - oauth

My web app checks for access token expiry from this header value "X-MS-TOKEN-AAD-EXPIRES-ON", and then it invokes the /.auth/refresh endpoint when the token expiry < DateTime.Now.
This has been working until now...
The problem now is that the "X-MS-TOKEN-AAD-..." headers are empty after expiry. Same with accessing the /.auth/me page which shows [].
Is there a change in Azure AD that would have caused this new behaviour?

Related

Using nodemailer & Google OAuth to send email, working for 7 days, but get invalid grant

I've been working to setup Oauth communication for an auto-emailing node.js web app using nodemailer. (I don't wish to use gmail's Less Secure Apps setting).
I've taken steps to get the client id, secret, and refresh token from the oauth playground, and have set up the web app to use a stored refresh token to request new access tokens when it first loads.
It is able to send emails (for about 7 days), then I get error invalid status code 400 on client side, and/or invalid grant on server side.
Going back to google playground and getting another refresh token, then updating it in environment variables, solves this for another week. But I'd like to solve this indefinitely.
I read somewhere "A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of 'Testing' is issued a refresh token expiring in 7 days"... so last week I switched the app to "In Production" (at console.cloud.google.com) and tried having it verified with google. This week, the same issue has recurred suggesting that wasn't the right fix, or that it wasn't yet verified with google.
I don't know if this was done correctly, nor do I know if this is the true solution to this expiring/revoked refresh token, or invalid grant.
I've also come across these explanations:
The user has revoked your app's access.
The refresh token has not been used for six months.
The user changed passwords and the refresh token contains Gmail scopes.
The user account has exceeded a maximum number of granted (live) refresh tokens.
The client has reached a limit of 50 refresh tokens per account if it's not a service account.
(I didn't make ANY changes during the week, so...not sure why these would have changed)
Is the issue the refresh token?
Or the status of the application?
Would it be dns/cname/cloudflare server issues?
For those who have the same issue in the future:
It turned out that google verification wasn't necessary.
It seems like the refresh token expiring after a week or 7 days was due to the placement of the oauth2Client.setCredentials() function call and accessToken variable.
Calling setCredentials() and obtaining the access token INSIDE the SendEmail() function (at runtime, just before sending email, rather than at application start/spinup time) seemed like it enabled the code to more dynamically generate the tokens it needed. After 12 days, it still seems like its working so I'd call this a success.
My guess at why it wasn't working before was because setting credentials outside of a function meant that code only ran once on server/application startup. It would then store the obtained access token in a const.
The access token would eventually expire, and even if called again/later inside of a function to obtain a new access token, it would be unable to change the value of a const property/variable, and so the call would inevitably fail after a week when it failed to renew.
Hope this helps anyone else having a similar issue.
My apologies for the run-on sentences.
There are a lot of causes for invalid grant it sounds to me like your refresh token is expiring.
If your project on google developer console is still in testing, has not been moved to published and has not gone though the google application verification process then refresh tokens have a max two week life span after which they will expire which may explain your invalid grant. The thing is there is no official word from google that this is happening its just what a lot of developers are seeing these days.
Another one is with gmail scopes if the user changes their password this will also cause the refresh token to expire.

Refreshed OAuth2 token has invalid signature (Azure AD OAuth2)

I'm trying to create an authentication flow where the user's access token is kept in a server-side session along with the refresh token, and when the token expires it is renewed if the session is still valid. However, the token I get back from Azure AD after refresh has an invalid signature, when verifying it with the same method as the original token.
Here's a runnable gist that illustrates the problem: https://gist.github.com/tlycken/fdaf47dc31e03de43a1a07fbbea2ab91
What I'm doing is basically this:
When the user requests a page, check for a session. If none exists, redirect to /auth which redirects to Azure AD, and when I'm returned I have a valid token which I store in the session.
Verify the token from the session using jwks-rsa. (This normally works fine, so I'm purposely adding something to the token string to make the signature invalid in the test code.)
If token verification failed, and there is a refresh token on the session, try to fetch a new token using that refresh token. This request normally returns with status 200 OK and a new set of access/refresh tokens.
Verify the new access token using the same code as was used to verify the old one (now without garbling the token). This should work, IIUC, but it fails with the error invalid signature.
Why does my newly refreshed token not pass verification?
Update:
I was able to create a simpler flow for reproducing this; the gist has been updated. It now does the following (printing these messages, along the way):
no session, redirecting to /auth
successful auth callback, redirecting to /
verifying old token
decoded user id e7f02a6e-510c-430d-905c-f8a0e63206c2
refreshing
fetching /me with renewed token
got user id e7f02a6e-510c-430d-905c-f8a0e63206c2
verifying new token
token verification failed: invalid signature
In addition to validating the token myself, I now also send a request to Azure with it, hoping that such a request would fail for an invalid token. But it passes!
You're code is using the v1 Endpoint to obtain the initial access token but the v2 Endpoint to exorcise the refresh token. These two endpoints operate differently. In particular, the v1 Endpoint uses "resource" while v2 uses "scopes".
The reason this is happening is your calling v1 explicitly but relying on the v2 /openid-configuration for the Refresh Token endpoint.
To correct this, change line 19 of refresh-auth-token.js to
const configResponse =
await fetch(`https://login.microsoftonline.com/${AZURE_TENANT}/.well-known/openid-configuration`)

Google OAuth access token expiration in MVC app?

I wrote an MVC app using Google Oauth2 as instructed here:
https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web_applications
I have an issue with access token expiration. When access token expires, I get the exception when calling Google API: "The access token has expired but we can't refresh it"
The initial authentication is two iterations mechanism:
first iteration AuthorizeAsync returns result with empty Credential, and populated RedirectUri:
So, the authorization url created is this:
https://accounts.google.com/o/oauth2/auth?access_type=offline&response_type=code&client_id=MYCLIENTID&redirect_uri=http:%2F%2Flocalhost%2FHomepage%2FAuthCallback%2FIndexAsync&scope=https:%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar https:%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly&state=http:%2F%2Flocalhost%2FHomepage%2F95419199
Note that access_type=offline is present. So I should get the refresh token back as well (doesn't happen).
second iteration - AuthorizeAsync returns result with populated Credential and empty RedirectUri:
Question1 - is RefreshToken supposed to be null at this moment?
The result is remembered, since it's defined as static.
Next request that comes in - the Calendar action that requires result.Credential to call Google Calendar API:
Question2 - if access token expires by that moment (for testing I just set ExpiresInSeconds = 0), I call RefreshTokenAsync method, but it always returns false! Why? What am I missing here?
And what would be the right way to handle when RefreshTokenAsync returns false?
Current RedirectResult(result.RedirectUri) command will fail since result.RedirectUri is null.
Oh, I finally got it :)
For those who interested - refresh token is only issued once, when you get that Consent screen, where you have to click Yes.
So, in order to get refresh token, go to your account setting, Account Permissions: https://security.google.com/settings/security/permissions
and revoke access for the project you configured in Google Developers Console: https://console.developers.google.com/project
Now, put a breakpoint on the next line after you call AuthorizeAsync, restart your application in Debug mode, get that consent screen asking for permissions, click Accept.
The app will return to VS and will stop on your break point.
Now, record somewhere the result.Credential.Token.RefreshToken value, it's an encrypted string.
I placed my in web.config appsetting for simplicity.
Now, I just assign that value back to result.Credential.Token.RefreshToken = refreshToken;
and every time, when access token expires, it will automatically refresh it.
Like here when I call GmailService request.Execute(...) passing the credential object that contains the token, the token will be refreshed.

FedAuth cookie does not display expiry date in Firebug

I have an ASP.Net MVC site secured with SSL and am using System.IdentityModel.Services and am creating the token like this:
SessionSecurityToken token = new SessionSecurityToken(myClaimsPrincipal, TimeSpan.FromDays(1));
SessionAuthenticationModule sam = FederatedAuthentication.SessionAuthenticationModule;
sam.WriteSessionTokenToCookie(token);
When I access the site in the browser, Firebug does not display the expiry date as expected. Instead the expiry date is shown as Session:
Can anyone explain why this is please? I assume that ASP.Net can still see the actual expiry date internally when it reads the cookie? More so, where is the cookie expiration time actually set?
You're mixing two different things here:
Token Expiration is determining until when the token is valid. After that time, even if the token is attached to a request, it is considered invalid and will not be honored. Usually the expiration time is encrypted within the token itself and that means that it's controlled solely by the token issuer.
Cookie Expiration is something that is controlled by the client (your Web-Browser in this case). Once the Cookie is expired it is no longer being attached to the request. But, should the Browser decide to send it, it will work until the Token expiration has reached.
In your particular case, the Token expiration is set to 1 day, but since the Cookie expiration is set to 'Session' it means that if you were to end the session (typically by closing your Browser window) at some point before the Token expires, the Cookie will not be sent and you'll be required to login again.
After 1 day (when the Token expires), even if you're still in session, you're always required to login again.
Update (as per your comments):
Ticket expiration and Cookie expiration can be set separately simply because sometimes the ticket is not necessarily contained in a Cookie. It may be sent to the server using other methods (QueryString, custom HTTP header etc). Yet, indeed the natural thing to do is have them both set to the same expiration time.
This is also the case in your SessionSecurityToken, if you'll set its IsPersistent flag to true you'll notice that Cookie expiration is now the same as the Ticket:
SessionSecurityToken token = new SessionSecurityToken(myClaimsPrincipal, TimeSpan.FromDays(1));
token.IsPersistent = true;

Update bearer token without logging out and in again using WIF

Quick summary... Here's a pseudo-flow to show what I'm trying to accomplish...
Client (angular) -> Server (webapi/WIF)
Log in with username and pwd -> Validate login credentials and return bearer token with claims
Change selected client -> Send new bearer token with updated claims based on selected client
When changing selected client, the existing bearer token will of course be sent, so the server can use that to know who I am and verify I can change to the selected client before generating a new bearer token to return
More detail...
I am using the default code generated by WIF to log users in and return a bearer token that is then sent with every request from my app (angular). I call the TOKEN endpoint with my username and password, GrantResourceOwnerCredentials is called, the user is authenticated, the claims are set, and the access_token is returned. All well and good.
Here's the scenario I don't know how to handle. When the user is logged in, they have the claims for the default client to which they are assigned. However, they can change the client they're accessing at any time while logged in, but each client comes with its own set of claims, meaning that the bearer token needs to be replaced with one for the selected client.
I keep thinking there's a "public string ReturnNewBearerToken(AuthenticationTicket ticket)" function somewhere that would let me get a new token and return it as a string. But I've scoured the web and can't find anything to do this. Everything I've seen suggests that you have to log out and log back in to update your token, which I can't believe is the case. I can't know what client the user wants to access the app as until the log in, but once they're logged in I can't update the token? That would be a catch 22.
So, does anyone know how to do this with WIF?
The answer is in my comments above. The jist is that the line...
var tokenStr = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
is what I needed to be able to return an encoded ticket as a bearer token to my angular app.

Resources