In an application I'm building, we're using JWT tokens as OAuth Bearer token.
Say we have a resource collection called things, addressable by thing ID, eg. things/1, things/44, etc.
Currently, whenever someone request an access token with the scope things, we include a list of all the rights the user has to each of the things it has rights to:
{
"sub": "Romeo",
"scope": [ "things" ],
"things": {
"1": [ "read", "write", "delete" ],
"44": [ "read", "write"],
}
// ...
}
This works fine, but things go bad when the user has a lot of things. Because all the rights are encoded inside the JWT token, the token gets really bigger for every thing the user has.
This is not scalable, and I need to find a solution for this. I could scope the tokens to belong to a single thing at a time, but then token management for a client that manages becomes a hell (I need a token that can list the tokens and need to keep one token per thing).
I can't get rid of Bearer tokens because some of our components are not able to talk to the token issuer for multiple reasons.
Is there a standard way to solve this problem? I was thinking about making tokens with the things scope interchangeable, so I can exchange restricted tokens that only have a part of the things in them for other tokens that have other parts of the things in them.
That information doesn't necessarily need to be in the token. As long as the token contains a reference to the user, the Resource Server can do a lookup in to some backend service/database to retrieve the corresponding permissions, associated with exactly the resource that is being requested at that time.
That service doesn't need to be the Authorization Server itself; it can also be a database or any type of backend system (probably the same that the Authorization Server would talk to).
I think #hans-z has it correct. oAuth does not solve this problem for you.
When you hit the pain barrier, I would suggest your implement a User-Service where "things" can be requested with the JWT token.
To postpone the pain barrier, you might consider changing to JSON Web Tokens or another implementation where token compression is supported or re-code the tokens with protobuf when sent over the network.
Related
I'm using OpenID Connect to control access to my REST API. One of the things I need to do when servicing a request is get the OIDC UserInfo based on the access token in request's Authorization: Bearer ... header.
To this point I've been working with JWTs and this works fine. I'm looking at expanding this to work with opaque tokens as well.
My strategy has been based on my understanding of the OpenID Connect Discovery spec, section 4:
Extract the iss from the access token.
Discover the userinfo endpoint by getting ${iss}/.well-known/openid-configuration and querying the JSON for userinfo_endpoint.
HTTP GET the userinfo_endpoint, passing the access token as an Authorization: Bearer ... header.
This works fine for opaque tokens... except for step 1. Currently, I have to know who the issuer is via an out-of-band mechanism because I don't know how to get the issuer from the opaque token (which, to be honest, makes sense given that it's opaque). I see a few possibilities:
Maybe I'm just supposed to know who issued it and my question is misguided.
Maybe the best thing to do is try a list of known issuers and see if one of them works.
Maybe there's a mechanism for discovering the issuer of the opaque token. (The spec refers to WebFinger, but that doesn't seem like it fits my use case.)
Maybe there's something I haven't considered...
Thanks all for any help.
The standard mechanism for dealing with opaque tokens is via introspection. Also the preferred option is for there to only be a single type of access token - issued by your Authorization Server (AS), which sits alongside your APIs.
The introspection result can be a claims payload or a JWT. It is common to plug in an API gateway, as in this article, so that the gateway makes the actual introspection call. The gateway should then cache results for subsequent calls made with the same access token.
An opaque token is typically a GUID or something similar, and the issuer value is not stored in the token - it is instead stored in the issuing Authorization Server's back end state. The only way to determine the issuer is to try to introspect the token.
FOREIGN ACCESS TOKENS
Aim to avoid using foreign access tokens in your APIs as in the following examples. This can make it difficult to control data added to tokens and token lifetimes:
User signs in with Google - then API uses Google access tokens
User signs in with Microsoft - then API uses Microsoft access tokens
It is preferred instead to use 'federated login capabilities' of your Authorization Server, leading to the following cleaner result, and fewer issues:
User signs in with Google - then API uses your AS access tokens
User signs in with Microsoft - then API uses your AS access tokens
Answering my own question:
You cannot discover anything from an opaque token alone.
An opaque token could be anything, even just a UUID that serves as a key to a database table in the Authorization Server. The only way anything can be obtained with such a token is by calling the introspection endpoint.
(Even if there is anything encoded in the opaque token, the only way it should be decoded is by calling the introspection endpoint.)
To that extent, my original question was somewhat misguided in that I was attempting to decode something that doesn't intrinsically express its encoding. It's rather like trying to use a pointer without knowing what it points to. Indeed, I've found a number of places where the term "reference token" is used to mean "opaque token."
Thanks to Gary Archer for some helpful feedback.
Does an app using authorization code (with or without PKCE) to obtain access + id tokens on behalf of a user need to also validate those tokens (signature, not expired, audience, etc.)?
If so, what for? Since the client is using TLS and pointing to the provider it's been configured with, what attacks/threats does that client also validating the token mitigate?
At first glance you are right and the spec basically allows for not checking tokens directly returned from the token endpoint over TLS as you suggest indeed, but:
Firstly, one may argue that if a signature is present, it is there for a reason and it should be validated since the Provider is also free to return tokens without a signature (alg="none") if it did not want/need the Client to validate.
Secondly, there are known attacks ("IDP mixup") that trick the Client into talking to the wrong token endpoint as a way of stealing the Authorization Code: verifying that the ID token returned does not match the expected signature would at least stop the Client from processing an ID token produced by an attacker.
Thirdly, it would be good for the Client to protect itself against broken or compromised IDPs in general, avoiding replay attacks or similar.
I guess when you're doing all of it in a single domain, i.e. Client, AS and RS are all under control of the same organisation and the relationships between them are fixed and one-to-one only, technically verification would be overkill based on current knowledge and known attacks.
But in case your use case spans multiple security domain, in general it is better to verify than it is to assume.
I am not sure which approach I should be taking in our implementation and need some guidance.
I have a REST API (api.mysite.com) built in the Yii2 Framework (PHP) that accesses data from mysite.com (database). On mysite.com our users will be able to create Connected Apps that will provision a client id + secret - granting access to their account (full scope?).
Based on my research, the next step seems to be setting up something to actually provide the bearer tokens to be passed to the api - I have been leaning towards oAuth2, but then I read that oAuth2 does not provide authentication. Based on this I think I need OpenID Connect in order to also provide user tokens because my API needs to restrict data based on the user context.
In this approach, it is my understanding that I need to have an Authentication Server - so a few questions:
Is there software I can install to act as an OpenID Connect/oAuth2 authentication server?
Are there specific Amazon Web Services that will act as an OpenID Connect/oAuth2 Authentication Server?
I am assuming the flow will be: App makes a request to the auth server with client id + secret and receives an access token. Access token can be used to make API calls. Where are these tokens stored (I am assuming a database specific to the service/software I am using?)
When making API calls would I pass a bearer token AND a user token?
Any insight is greatly appreciated.
your understanding is not very far from reality.
Imagine you have two servers one for Authentication, this one is responsible for generating the tokens based on a Authorization Basic and base64 encoded CLientID / ClientSecret combo. This is application authentication basically. If you want to add user data as well, simply pass username / password in the post body, authenticate on the server side and then add some more data to the tokens, like the usernames, claims, roles, etc
You can control what you put in these tokens, if you use something like JWT ( Json Web Tokens ) then they are simply json bits of data.
then you have a Resource server, you hit it with a Authorization Bearer and the token you obtained from the Authorization one.
Initially the tokens are not stored anywhere, they are issued for a period of time you control. You can however do something else and store them in a db if you really want to. The expiration is much safer though, even if someone gets their hands on them they won't be available for long! In my case I used 30 minutes for token validity.
Now, you haven't specified what languages/frameworks you are looking into. If you use something like dot net then look into IdentityServer, version 4 is for Dot net core, 3 for anything below.
I also have a pretty long article on this subject if you are interested:
https://eidand.com/2015/03/28/authorization-system-with-owin-web-api-json-web-tokens/
Hopefully all this clarifies some of the questions you have.
-- Added to answer a question in comments.
The tokens contain all the information they need to be authenticated by the resource server correctly, you don't need to store them in a database for that. As I already said, you can store them but in my mind this makes them less secure. Don't forget you control what goes into a token so you can add usernames if that's what you need.
Imagine this scenario, you want to authenticate the application and the user in the same call to the Authorization Server. Do the OAuth2 in the standard way, which means authenticate the application first based on the client id / client secret. If that passes then next do the user authentication. Add the username or userid to the token you generate and any other bits of information you need. What this means that the resource server can safely assume that the username passed to it in the token has already been validated by the authentication server otherwise no token would have been generated in the the first place.
I prefer to keep these two separate myself, meaning let the AS ( Authorization Server) to deal with the application level security. Then on the RS (Resource Server) side you have an endpoint point like ValidateUser for example, which takes care of the user validation, after which you can do whatever you need. Pick whichever feels more appropriate for your project I'd say.
One final point, ALWAYS make sure all your api calls ( both AS and RS are just apis really ) are made over HTTPS and never ever have any important information transmitted via a GET call which means the URL can be intercepted. Both Headers and POST body are encrypted and secure over HTTPS.
This should address both your questions, I believe.
I am developing an oAuth2 server and I've stumbled upon this question.
Lets suppose a scenario where my tokens are set to expire within one hour. On this timeframe, some client goes through the implicit auth fifty times using the same client_id and same redirect_uri. Basically same everything.
Should I give it the same accessToken generated on the first request on the subsequent ones until it expires or should I issue a new accessToken on every request?
The benefits of sending the same token is that I won't leave stale and unused tokens of a client on the server, minimizing the window for an attacker trying to guess a valid token.
I know that I should rate-limit things and I am doing it, but in the case of a large botnet attack from thousands of different machines, some limits won't take effect immediately.
However, I am not sure about the downsides of this solution and that's why I came here. Is it a valid solution?
I would rather say - no.
Reasons:
You should NEVER store access tokens in plain text on the Authorization Server side. Access tokens are credentials and should be stored hashed. Salting might not be necessary since they are generated strings anyway. See OAuth RFC point 10.3.
Depending how you handle subsequent requests - an attacker who knows that a certain resource owner is using your service and repeat requests for the used client id. That way an attacker will be able to impersonate the resource owner. If you really return the same token then at least ensure that you authenticate the resource owner every time.
What about the "state" parameter? Will you consider requests to be the "same" if the state parameter is different? If no then a botnet attack will simply use a different state every time and force you to issue new tokens.
As an addition - generally defending against a botnet attack via application logic is very hard. The server exposing your AS to the internet should take care for that. On application layer you should take care that it does not go down from small-bandwidth attacks.
You can return the same access_token if it is still valid, there's no issue with that. The only downside may be in the fact that you use the Implicit flow and thus repeatedly send the - same, valid - access token in a URL fragment which is considered less secure than using e.g. the Authorization Code flow.
As a thumb rule never reuse keys, this will bring additional security in the designed system in case of key capture
You can send different access token when requested after proper authentication and also send refresh token along your access token.
Once your access token expires, you should inform user about that and user should re-request for new access token providing one-time-use refresh token previously provided to them skipping need for re-authentication, and you should provide new access token and refresh token.
To resist attack with fake refresh token, you should blacklist them along with their originating IP after few warnings.
PS: Never use predictable tokens. Atleast make it extremely difficult to brute force attacks by using totally random, long alpha-numeric strings. I would suggest bin2hex(openssl_random_pseudo_bytes(512)), if you are using php.
I am currently in the process of building an OAuth2 provider using the bshaffer PHP library here.
I've found IETF draft specifications that outline the implementations that specifically call out the usage of JSON Web Tokens as an authorization grant and client authentication.
The implementation that interests me however is returning a JWT in place of the regular access token, as seen here. In case of dead link, the access token response is pasted below.
{
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpZCI6IjYzMjIwNzg0YzUzODA3ZjVmZTc2Yjg4ZjZkNjdlMmExZTIxODlhZTEiLCJjbGllbnRfaWQiOiJUZXN0IENsaWVudCBJRCIsInVzZXJfaWQiOm51bGwsImV4cGlyZXMiOjEzODAwNDQ1NDIsInRva2VuX3R5cGUiOiJiZWFyZXIiLCJzY29wZSI6bnVsbH0.PcC4k8Q_etpU-J4yGFEuBUdeyMJhtpZFkVQ__sXpe78eSi7xTniqOOtgfWa62Y4sj5Npta8xPuDglH8Fueh_APZX4wGCiRE1P4nT4APQCOTbgcuCNXwjmP8znk9F76ID2WxThaMbmpsTTEkuyyUYQKCCdxlIcSbVvcLZUGKZ6-g",
"client_id":"CLIENT_ID",
"user_id":null,
"expires":1382630473,
"scope":null
}
It returns a JWT in place of the regularly generated access token for the normal authorization grants. Client and User credentials grants are the more important for me as we are only dealing in 1st party API access.
This implementation seems to be ideal, because I do not need to maintain a store of generated tokens, limiting the amount of infrastructure that is required. At some point if we open the API to third-parties we would need a key-store for the various pub/priv keys to validate each client's tokens, and to limit the risk if some nefarious party stole the encryption key.
I feel this is a good implementation relying on asymmetric encryption and SSL/TLS. However are there potential security risks I've missed?
The signature on the JWT will only protect any claims inside the token from tampering, but cannot protect claims external to the token. Therefore, the expires field in your structure is not protected and can be tampered with.
To protect from tampering, you want to use the exp claim.
Two valid solutions are:
double check expires against exp
drop expires and just use exp
You might prefer one over the other depending on your requirements. Personally, I'd keep it simple and go with (2)