I'm using the python requests-oauthlib package to connect to the Microsoft Graph. I am using the OAuth 2.0 Client Credentials flow.
The following simplified code works perfectly fine:
from oauthlib.oauth2 import BackendApplicationClient
from requests_oauthlib import OAuth2Session
client = BackendApplicationClient(client_id='myclientid')
token_url = "https://login.microsoftonline.com/mydomain.onmicrosoft.com/oauth2/v2.0/token"
msgraph = OAuth2Session(client=client)
msgraph.fetch_token(
token_url = token_url,
client_secret = 'myclientsecret',
scope='https://graph.microsoft.com/.default')
response = msgraph.get(
url="https://graph.microsoft.com/v1.0/users/user#mydomain.com/messages")
While this works, the Bearer access token in this case is only valid for 1 hour. The requests-oauthlib package has support for refreshing tokens but it seems limited to token types that come with separate refresh tokens. The client credentials flow as used with the Microsoft Graph only issues an access_token.
So my questions are:
Is there a way to make the requests-oauthlib refresh the token automatically in this use case or do I need to manually track the age of my token and explicitly refresh it as needed?
I'm not wedded to requests-oauthlib so if there is a better library that accomplishes the auto-refreshing I'd be interested in using it.
This behavior is by design (and aligns with the OAuth spec). The only OAuth grants that support Refresh Tokens are Authorization Code and Resource Owner Password Credentials. The Implicit and Client Credentials grants only return an Access Token.
More importantly, since the Client Credentials flow isn't interactive, there is no need for Refresh Tokens. You simply request a new token when the old one expires.
As far as I can tell, there is still no built-in way to do this automatically using requests-oauthlib. There is a ticket about it on their GitHub with a couple of different ideas on how to do it, but nothing out of the box: https://github.com/requests/requests-oauthlib/issues/260
I know this is an old question, but it seems unanswered, so please allow me to give it a try.
My initial answer was:
I dare make the hypothesis, reading your mention of lack of refresh token, that you did not add offline_access in your requested scope - if you want it to be part of the answer from the Microsoft authentication service, you have to (please refer to https://learn.microsoft.com/en-us/graph/auth-v2-user#token-response and the various pages around for more details).
which was indeed totally irrelevant for the scenario used, as commented by Mark, and also clearly stated in https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/resilience-daemon-app#cache-and-store-tokens:
It is important that applications use the "expires_in" property to determine the lifespan of the token.
So as an answer to your question 2., the above link also suggests the use of MSAL (MS Authentication Library):
MSAL implements and follows [best practices for caching and storing tokens] automatically.
what the why use MSAL wiki page seems to confirm:
It also adds value by [...] maintaining a token cache and refreshes tokens for you when they are close to expire. You don't need to handle expiration on your own.
For your question 1., I indeed did not find a standard way in requests-oauthlib to do so, either.
In this kind of situation, I usually don't monitor the age of the token, but just catch the 401 return code and fetch a new token.
To do so, I found suitable to tweak the first example of the Requests-OAuthlib - OAuth 2 Workflow - refreshing tokens section, replacing their call to refresh_token(refresh_url, **extra) by a new call to fetch_token().
What I usually use in order to avoid repeating the try...except... code piece, is to put it in a wrapper decorator (got a good inspiration in https://realpython.com/primer-on-python-decorators/#a-few-real-world-examples ) around my API-calling functions / methods.
Hope this time it helps more...
Related
I have a project that uses node-oidc-provider and angular-oauth2-oidc.
However one thing is wierd, refresh token request uses prompt=consent (I know it is by spec) which returns 303 with location including code in the hash, the token is refreshed but it looks like terrible UX if SPA appplication refreshes in middle of user interaction, is this expected behaviour or is something in my configuration wrong?
Is there any way to get refresh token through backchannel like AJAX request (I would like to avoid iframes if possible)? I can't find any specs on how it should work.
OPTION 1
The traditional SPA solution is to use an Authorization Code Flow (PKCE) redirect on a hidden iframe using prompt=none. This prevents refreshing the SPA since it runs in a mini app as in this code. This is no longer reliable though, due to recent browser restrictions that drop the SSO cookie - eg in Safari.
OPTION 2
Another option is to use a refresh token grant message in an Ajax request. But this relies on storing a refresh token in browser local storage to get past page reload issues. And this is not considered secure and is not likely to fare well in PEN tests etc.
OPTION 3
The preferred option these days is a variation on option 2 where the refresh token is stored in a secure HTTP only encrypted cookie. It is possible to issue cookies via an API, if you want to avoid impacting the web architecture, though it is a little tricky. See this Curity blog post for more on this approach - and this code sample.
I'm in the process of rebuilding an existing web app, that uses JWTs to manage authentication. I'm still new to JWTs, so I'm learning about how they should work, while, at the same time, trying to understand why the web app's current implementation is the way it is.
The current version's flow is as follows:
When a user successfully logs in or registers, a user object is returned along with a JWT property. This JWT is included in subsequent API calls as an Authorization header.
Every ten minutes, a get request is made to API endpoint /refresh-token.
If successful, the response body contains a success message, and the response header contains an updated Authorization header.
All subsequent ten-minute timed get requests to /refresh-token use the original JWT that was returned in step 1, and so on.
From what I've read so far, this doesn't correlate with any recommended approaches.
Is it safe enough to replicate this flow in the newer version, or is this something I'm better off not replicating?
Edit: I'm working solely on the front-end - the API isn't being updated for a while, so I'm limited to what it currently returns.
I believe this article summarizes the current state of the art: https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/. You usually have two tokens. Access token which is short lived and an refresh token, which lives longer. This way you don't need to call the auth server every x minutes, but you can do it on demand.
I don't know if you need to deal with blacklisting too? I believe blacklisting is easier when you have a separation of access token and refresh token (only refresh token needs to be blacklisted). But I believe you could deal with this problem too, probably in a bit more sophisticated manner.
Having said that. What you have is not wrong. It's hard for me to point out any flaws in the way you are doing besides of what has been pointed out above.
I'm currently trying to figure out what the difference between gapi oauth access tokens and access tokens created via the googleapis package is. There apparently is a difference, since when I try to use the access token generated by gapis to authenticate to google picker using setOAuthToken() only the gapi access token works. This is unfortunate, since I need to refresh tokens on the backed and I therefore cannot use gapi. When I try to use the googleapis token, I get prompted to sign in, which is also not very ideal.
The scopes for both are exactly the same and the client_id is the same. The only idea I have is, that it might have to do with the googleapis one being an offline token, but that shouldn't matter right?
The code for the picker is pretty much straight from their docs:
picker = new google.picker.PickerBuilder()
.setCallback(pickerCallback)
.setDeveloperKey(apiKey)
.addView(google.picker.ViewId.SPREADSHEETS)
.setOAuthToken(access_token)
.build();
Anyone know what's going on here?
I have also seen a couple other people ask similar questions, but either their answers didn't fix anything (probably because some of the problems date back to 2012 and earlier).
EDIT: Nevermind. Somehow it resolved itself. I really have no idea why....
I’m learning to code for one year now. I mainly learned how to deal with a rest API (Node/Express on back-end and Vue on front-end).
I get to the point where I want to develop the ideas I have for app.
For this, I first wanted to develop the backend to have a authentification process that I could use as a boilerplate for other project I would have.
But now I’m completely lost with Jsonwebtoken and how to exactly use it in order to make something secure as well as user-friendly.
So far I understand that a rest API should be stateless (I.e. nothing should be store server-side and should therefore not have DB calls -as for sessions- to grant access to data).
In this respect, I’ve noted different strategies :
Short-lived JWT : (+) this is very secure since you theoretically have to log in every time you want to access the server (-) very bad user experience
Long-lived JWT : (+) user-friendly (persistent login) (-) very insecure (no way to check if JWT was stolen)
Short-lived JWT with Long-lived Refresh Token…
That’s where I get confused…
From every articles/tutorials I’ve read, the refresh token should be linked somehow with a DB (e.g. to store the refresh token key or the blacklisted token…). I’ve also seen a tutorial that partly linked the secret key (to verify the token) with the hashed password stored in the DB. This is kind of smart since previous token will automatically be considered as invalid as of the moment the user changes his password… But this once again mean a DB call…
My point is that I’m coming to the conclusion that
(1) there’s no perfect way to handle authentification process in secure and user-friendly way…
(2) DB calls cannot be avoided to have something a bit secure...
And considering that conclusion, I definitely can’t understand the use of refresh token…
If refresh tokens required DB calls, you could get to the same result with only one main token…
You could for instance store a JWT ID in the token and in the DB… If those two id match upon validation of the token, you issue a new token with a new id that overwrites the previous one… Now if you use an old one, it will never be validated… Then, since you have called the DB to validate the token (most certainly the USER table), you could check in the sametime if, for example, the user is an admin or not (no need to store it in the JWT)… Finally, you could use the « hashed password » trick described above to enhance security…
So… What am I missing ? What is the best strategy ?
I’ll be happy to have your comments on this (or a link to a very good article - I’ve a lot of these though…)
Thank you very much for your help
PS: and I’m not even talking about how to send the token to the server (with cookie but risk of CSRF attach or with header but subject to XSS attack if token is stored client-side)… In this respect I’ve seen multiple tutorial that use JWT through cookie with cerf key stored client side as well as inside the jet => both should be send.
PS2: I hope I'm clear since I'm french-speaking native :-)
So you have asked quite a few questions in this one question. It will be quite difficult for anyone to give a thoughtful answer here, but I shall try. Before that, full disclaimer, that I am the author of a new library called SuperTokens that I believe would provide you the best solution for session management. It's possible that you may have already read our blog: https://hackernoon.com/all-you-need-to-know-about-user-session-security-ee5245e6bdad. It's best if we can chat about this so that I can give you a full detailed explanation about everything you have asked (I would love to help out). Join our discord: https://discord.gg/zVcVeev
Now to answer your question(s):
You should always only use short lived JWTs
Doing a database call for authentication is not a problem, but as everything else, we try and optimise things, so doing fewer db calls, is better. If you go with JWT access tokens and Opaque Refresh tokens, then for most APIs, you do not need to do a db call. However, if you link the secret key of the JWT with the hashed password, then you would have to a db call for every API - which is OK, but I feel unnecessary since you are going to use short lived JWTs anyways (a few hours to a day max)
You mentioned token theft - this is a tricky issue, but according to RFC 6819, you can use the concept of rotating refresh token to detect theft. Of course, actually doing so can be tricky as you have to take care of many race conditions and network failure issues - see https://hackernoon.com/the-best-way-to-securely-manage-user-sessions-91f27eeef460
About why we need refresh tokens: Say you do not have a refresh token, then you would have just one token that you could treat as both, an access and a refresh token. You could still make it short lived (when it's an access token), and then after it expires, treat it as a refresh token - which also has some lifetime. The problem with this is that it's not very clean (point of expiring a token is that it is useless afterwards), and that you are exposing the refresh token over the network every single API call - reducing security. I know you can have HTTPS, but there are HTTPS MITM attacks as well - see the blog post, part 1.
In terms of storage, one neat trick could be to split the access token into two - one to store in secure, httponly cookie, and one to store in localstorage. For every API call, send both to the server (cookies will be sent automatically anyways), and then the server would combine the two and go about authenticating. This would prevent both, CSRF and XSS attacks on sessions!
Now, you could either implement this whole thing on your own, or then use our library that does all these things out of the box: https://github.com/supertokens/supertokens-node-mysql-ref-jwt.
To discuss this more, or any other questions you have, join our discord server.
PS: I know I used this for advertising my lib, but I hope I did answer your question. It is genuinely difficult to give a good explanation to your questions without having a conversation.
I'm working on a RESTful(ish) API that has the following authentication style:
A client calls an "authenticate" API method and passes a username and password over HTTPS POST. This method returns basic account information and a "client token", which is stored on the user account in the database.
All further API calls (all over HTTPS POST) require a client token. If the system can't find the requester by client token, the call is rejected.
My open questions are:
1) Does anyone see a major security problem with this?
2) Is there any good reason why I should have client tokens expire over time or change? Right now I assign a random one to every user. If the user does a logout or forgot password, I generate a new one.
I'd love to know everyone's thoughts on this approach. I'm not going for innovation, I'm just making I'm aware of the risks on this approach.
What you've described is functionally equivalent to a session cookie, only reimplemented in your application, and therefore subject to a number of pitfalls that have likely already been dealt with by most web frameworks.
Ensure your tokens have enough bits of entropy. If the tokens are simple 32-bit integers, wild guesses might be enough to hit on one in use by someone else.
If you're generating these tokens randomly, ensure you use a cryptographically-strong source of random numbers, or the next token might be guessable based on previous tokens.
If these POST requests are coming from scripts and such embedded in web pages, passing the token around as an explicit parameter instead of as a cookie declared secure and httponly makes token-stealing by cross-site scripts much easier.