I'm writing a web service which uses OAuth2 for authorization. I'm using C# and WCF, though this isn't really pertinent to my question. Having never used OAuth before, I've been doing my research. I'm on the "verify they're actually authorized to use this service" end of things. I think I have a pretty good idea of how OAuth2 works now, but one aspect of it still confounds me.
OAuth2 is token-based. The token is just text and contains some information, including a "secret" that only your application (the web service, in my case) and the Authorization Server knows. The secret can be just a text phrase or a huge string of semi-random characters (sort of like a GUID). This "proves" the user contacted the Authorization Server and got the secret from it. What confuses me is this only seems to prove that the user contacted the Authorization server sometime in the past. In fact, it doesn't even prove that. It just proves that the user knows the secret. The rest of the token (such as role, duration, other stuff) can all be faked. Once the user gets one token for the service he wants access to, they could whip up new tokens with falsified information as much as they like. In fact, there could be numerous servers set up with thousands of "secrets" for nefarious individuals to use at will. Of course, this wouldn't happen often, but it seems very possible.
Am I correct, or is there something I'm missing? Is this a known weakness of OAuth2?
Whenever the Resource Server (or "service") receives a token, it needs to validate it. Depending on the token type it can check its signature that was created with the private key that Authorization Server has or it can call into the Authorization Server to validate the token. This way a user cannot forge a token: it would be impossible to forge the signature or to make the Authorization Server verify a token that it did not issue.
FWIW: you seem to be conflating the "token" and "client secret" and perhaps even the private key of the Authorization Server; they're all different concepts.
Related
Ok, user authenticates and client gets the JWT from my IS4 instance. All that works. Now, for reasons I still cry at nights after being tormented by people who authoritatively claim to know OAuth but do not, the client is sending me the identity token JWT over the wire to an action, and I need to do some work based on the subject in it. I want to minimize the fallout of this decision and prevent a situation where someone plants me a fake token, so I want to validate the JWT to make sure it came from me, that indeed I am the one who issued it. To simplify, I need to act as both the client and the server in the token validation process, while running on the IS4.
Since this is such a violation of OAuth protocol, I am not sure this is supported out of the box, but here comes: is there a way to do this? I even tried to introspect the token, but that requires an authentication context, and I can't seem to get the client credential flow working since I only use openid/profile scopes and they are not supported by the client credential flow (since the user is defined only in JWT).
The receiver of a token should always validate the signature of the token to make sure it came from your IdentityServer. This is usually automatically done by most proper JWT-libraries. The library will download the public-key from your IdentityServer and use it to verify the signature of the token.
If you are using ASP.NET, then the JwtBearer library will do that for you.
I'm looking for guidance and/or best practices in implementing step-up authentication. The generic scenario is as follows: user logs in to web app using some identity provider, user then goes to some specific area of web site which needs to be protected by additional MFA, for example OTP. All functionality for the website is via REST API, authenticating with JWT bearer token.
The best description of the flow I found is from Auth0 here. Basically, user acquires the access token with scope which is just enough to access APIs that do not require additional protection. When there is a need to access secure API the authorization handler on backend would check if the token has the scope indicating that the user has completed the additional MFA check, otherwise it's just HTTP 401.
Some sources, including the one from Auth0, suggest using amr claim as an indication of passed MFA check. That means that identity provider must be able to return this claim in response to access token request with acr_values parameter.
Now, the detail that is bugging me: should the frontend know in advance the list of API that might require MFA and request the elevated permissions beforehand, or should frontend treat the HTTP 401 response from backend as a signal to request elevated permissions and try again?
Should identity provider generate relatively additional short-lived token to access restricted APIs? Then, if frontend has 2 tokens it must definitely know which token to use with which API endpoint. Or maybe identity provider can re-issue the access token with normal lifespan but elevated permissions? Sounds less secure then the first approach.
Finally, do I understand the whole process right or not? Is there some well documented and time-tested flow at all?
CLIENTS
Clients can be made aware of authentication strength via ID tokens. Note that a client should never read access tokens - ideally they should use an opaque / reference token format. Most commonly the acr claim from the ID token is used.
This can be a little ugly and a better option can sometimes be to ask the API, eg 'can I make a payment'? The client sends an access token and gets a JSON response tailored to what the UI needs. The API's job is to serve the client after all.
APIs
APIs receive JWT access tokens containing scopes and claims. Often this occurs after an API gateway swaps an opaque token for a JWT.
The usual technique is that some logic in the Authorization Server omits high privilege ones, eg a payment scope or claim, unless strong authentication was used.
It is worth mentioning plain old API error responses here. Requests with an insufficient scope typically return 403, though a useful JSON error code in an error object can be useful for giving the client a more precise reason.
STEP UP
As you indicate, this involves the client running a new code flow, with different scope / claims and potentially also with an acr_values parameter. The Curity MFA Approaches article has some notes on this.
It should not be overused. The classic use case is for payments in banking scenarios. If the initial sign in / delegation used a password, the step up request might ask for a second factor, eg a PIN, but should not re-prompt for the password.
CLIENTS AND ACCESS TOKENS
It is awkward for a client to use multiple access tokens. If the original one had scope read then the stepped up one could use scope read write payment and completely replace the original one.
In this case you do not want the high privilege scope to hang around for long. This is yet another reason to use short lived access tokens (~ 15 minutes).
Interestingly also, different scopes the user has consented to can have different times to live, so that refreshed access tokens drop the payment scope.
ADVANCED EXAMPLE
Out of interest, here is an interesting but complicated article on payment workflows in Open Banking. A second OIDC redirect is used to get a payment scope after normal authentication but also to record consent in an audited manner. In a normal app this would be overkill however.
Watching this video, it details in OAuth2 that the client application first has to get the authorization grant from the Authorization server and then use that grant to get a token before being able to access the resource server. What purpose does the grant serve? Why not give the client the token right away after the user signs on with his/her username and password?
Because it is more secure, for some application types.
What you describe is so called authorization-code-flow. It is normally used for "classical" web applications, where only the backend needs to access resource server. The exchange of authorization code to access token happens on the backend and access token never leaves it. Exchange can be done only once and in addition client id and secret (stored on the backend) are necessary.
Single-Page-Applications often use implicit-flow where access token is delivered to the frontend directly in the URL.
See more here:
IdentityServer Flows
EDIT: Q: "I still don't see how it is more secure given that you have to have the grant in order to get the token. Why need 2 things instead of just 1 thing to access the resource? If someone steals the token, they can access the resource anyway – stackjlei"
"Stealing" access token will work independent on how your application acquires it. However, stealing access token on the backend is much more difficult than on the frontend.
Authorization code is delivered to the backend also over the frontend but the risk that someone intercepts and uses it is tiny:
It can be exchanged only once.
You need client-id and client-secret in order to exchange it. Client-secret is only available on the backend.
Normally, authorization code will be exchanged by your backend to access-token immediately. So the lifetime of it is just several seconds. It does not matter if someone gets hold of used authorization code afterwards.
In your scenario there could be two servers, an Authorization and a Resource one.
It could be only one as well, but let's imagine this scenario.
The purpose of the Authorization Server is to issue short lived access tokens to known clients. The clients identify themselves via their CLientID and CLientSecret.
The Authorization Server ( AS ) holds the list of clients and their secrets and first checks to make sure the passed values match its list. If they do, it issues a short lived token.
Then the client can talk to the Resource Server ( RS ), while the token is valid. Once the token expires, a new one can be requested or the expired one can be refreshed if that is allowed by the Authorization Server.
The whole point here is security, Normally, the access tokens are passed in the Authorization header of the request and that request needs to be over https to make sure that the data can't be stolen. If, somehow, someone gets hold of an access token, they can only use it until it expires, hence why the short life of the tokens is actually very important. That's why you don't issue one token which never expires.
You have different type of OAuth. On type doesn't require to use the 'grant' authorization. It depend who are the user/application, the ressource owner and the server API.
This way, you - as a user - don't send the password to the application. The application will only use the grant token to gain access to your ressources.
I think this tuto is a pretty good thing if you want more details
https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2
I'm working to implement a OAuth 2.0 server, and while reading the RFC6749 specification I realized that section 6 on Page 47 regarding "Refreshing an Access Token". Explains that we need to just use the Refresh Token that we have to get a new Token.
But for example, in addition to the Refresh Token, Google require the User ID and the Secret to do so.
This confuses me, because on one hand we have Google that is processing high volume of requests every day, and we have a specification written probably with a smaller scope in mind.
Is it good to send the Secret every hour with the Refresh Token?
Personally I believe no: because the User ID and Secret should be used only to go over the whole OAuth 2.0 process.
Basically
You use the token on each request to prove that you are who you are.
Refresh Token get used only once an hour (and potentially changed at each refresh)
Secret and User ID go to the internet as rarely as possible. Only when option 1 and 2 get compromised.
I personally believe that sending the Secret with the Refresh token is less secure. But maybe I'm missing something.
If you have another point of view, please share it :)
I might be missing something, but what Google requires and what's also specified by OAuth2 is that when refreshing a token from a confidential client application the client must authenticate itself.
The most common type of credentials being used for confidential clients are a client identifier alongside a client secret. This information is issued to a client application and is unrelated to the end-user.
By requiring client authentication the authorization server can be sure the request comes from a specific client and adjust its response accordingly. For example, an authorization server can decide that certain permissions - scopes - can only be requested from confidential clients.
The argument around reducing the number of times the client secret needs to be sent over the wire is a non-issue. OAuth2 mandates that the communication happens over TLS and if you have issues with sending the secret then you would also have issues with sending bearer access tokens.
In conclusion, although sometimes doing things exactly according to spec without questioning the overall context might lead to vulnerabilities:
... some libraries treated tokens signed with the none algorithm as a valid token with a verified signature. The result? Anyone can create their own "signed" tokens with whatever payload they want, allowing arbitrary account access on some systems.
(source: Critical vulnerabilities in JSON Web Token libraries)
Some libraries treated the none algorithm according to spec, but ignored the usage context; if the developer passed a key to verify a signature it most likely did not want to treat unsigned tokens as valid.
However, passing the secret on the refresh token request is not one of these situations so don't worry about it.
Studying OAuth2.0 I finally found these 2 refs:
RFC6749 section 2.3,
RFC6749 section 10.1
Correct me if I'm wrong:
It's possible to use unregistered clients, but you have to manage them yourself with security risks.
How should I manage them?
Some more specific questions:
A Native Application (a Public Client indeed) is not able, by definition, to safely store its credentials (client_id + secret). Is it an unregistered client? If I can't verifiy/authenticate it using a secret, what else should I do?
client registration ≠ endpoint registration: the first is about registering Client Credentials (client_id + secret); the second about registering Client Redirection Endpoints. Is the Redirection Endpoint registration sufficient to grant the authenticity of the Client?
Does Client Credential Grant use the same credentials (client_id + secret) for client registration?
I think you could answer me by explaining what does this paragraph (RFC6749 section 10.1) mean.
Please give me some references and practical examples on how to implement the interaction between the authorization server and the resource server.
Thanks
tl;dr:
Native clients cannot be authenticated with client_id and client_secret. If you need to authenticate the client, you'll have to implement an authentication scheme that doesn't entrust the shared secret to the client (or involve the end-user in the client authentication discussion). Depending on your application's security model, you might not need to authenticate the client.
The redirection endpoint is not generally sufficient to authenticate the client (though exceptions exist).
The "client credential" grant type may use any client authentication mechanism supported by the authorization server, including the credentials given out at client registration.
The gist, as I read it, is that you can trust a confidential client's client_id (read: "username") and client_secret (read: "password") to authenticate them with your service. There is no[1] chance that a third-party application will ever represent itself with that client's credentials, because they are reasonably assumed to be stored safely away from prying eyes.
A public client, however, can make no such guarantee – whether a browser-based application or a native desktop application, the client's id and secret are distributed to the world at large. It's quite reasonable to assume that such applications will end up in the hands of skilled developers and hackers who can dig into the client and extract the id and secret. For this reason, Section 10.1 explicitly states that:
The authorization server MUST NOT issue client passwords or other
client credentials to native application or user-agent-based
application clients for the purpose of client authentication.
Okay. So public clients cannot be authenticated by password. However…
The authorization server MAY issue a client password or other
credentials for a specific installation of a native application
client on a specific device.
This exception works because it ties the authentication of the client to a specific device, meaning that even if someone walked away with the client's secret, they couldn't reuse it. Implicit in this exception, however, is that the "specific installation … on a specific device" must be uniquely identifiable, difficult to spoof, and integral to the authentication process for that client.
Not every native application can meet those criteria, and a browser-based application certainly cannot, since there's nothing uniquely identifiable or difficult to spoof in the environment in which it runs. This leads to a couple of options – you can treat the client as unauthenticated, or you can come up with a more appropriate authentication mechanism.
The key to the authentication dance is a shared secret – something that's known only to the authorization server and the authenticating client. For public clients, nothing in the client itself is secret. Thankfully, there are options, and I'm not just talking about RFID key fobs and biometrics (though those would be completely acceptable).
As a thought experiment, let's consider a browser-based client. We can reasonably assume a few things about it: it's running in a browser, it's served from a particular domain, and that domain is controlled by the client's authors. The authentication server should already have a Client Redirection URI, so we've got something there, but as the spec calls out:
A valid redirection URI is not sufficient to verify the client's
identity when asking for resource owner authorization but can be
used to prevent delivering credentials to a counterfeit client
after obtaining resource owner authorization.
So the redirection URI is something we should check, but isn't something we can trust, in large part because the domain could be spoofed. But the server itself can't be, so we could try to ask the domain something that only the client's domain's server would know. The simplest way to do this would be for the authentication server to require a second ("private") URI, on the same domain as the client, where the client's secret will be hosted. When the client application makes an authorization request, the server then "checks in" against that second URI relative to the client's reported hostname, and looks for the shared secret (which should only ever be disclosed to the authorization server's IP address) to authenticate the client.
Of course, this is not a perfect solution. It doesn't work for every application, it's easy to get wrong, and it's potentially a lot of work to implement. Many potential authentication mechanisms (both highly specific and highly general) exist, and any one which does not entrust the client application with private data would be suitable for this problem space.
The other option we have is to implement no further authentication, and treat the client as unauthenticated. This is notably not the same thing as an unregistered client, but the difference is subtle. An unregistered client is a client whose identity is unknown. An unauthenticated client is a client whose identity is known, but untrusted. The security implication for both types of clients is the same: neither should be entrusted with private data. Whether the authorization server chooses to treat these two cases the same, however, seems to be left up to the implementer. It may make sense, for example, for an API to refuse all connections from an unregistered client, and to serve public read-only content to any registered client (even without verifying the client's identity).
Pragmatism, however, may yet win out – an unauthenticated client is fundamentally no different than the SSL "errors" you'll occasionally see when your browser cannot verify the authenticity of the site's SSL certificate. Browsers will immediately refuse to proceed any further and report exactly why, but users are allowed to accept the risk themselves by vouching for the identity of the server. A similar workflow may make sense for many OAuth2 applications.
Why is it important to verify the client's identity, anyway? Without doing so, the chain of trust is broken. Your application's users trust your application. The authorization workflow establishes that your users also trust the client, so your application should trust the client. Without validating client identity, another client can come along and assume the role of the trusted client, with all of the security rights thereof. Everything about client authentication serves to prevent that breach of trust.
Hope this helped!
[1]: Server compromises, where the source code of your application falls into malicious hands, are an exception to this, and there are other safeguards built-in for that case. Having said that, the spec also specifically calls out that a simple username/password combination isn't the safest option:
The authorization server is encouraged to consider stronger
authentication means than a client password.