I'm trying to use OAuth. In this example the provider was Windows Live. The redirect returned this fragment. I have added line breaks before the ampersands to make it a bit easier to read.
access_token=EwAwA61DBAAUGCCXc8wU/zFu9QnLdZXy%2bYnElFkAAQcQQB3c7oVYQmQhyeTOiw1Tp5iA7cjrLJbEnvXVoDlu48mjv7WX35RRIK3L3miAQEUrn5ZVNRSUV8dqiZi4kIko93k2bODqpIY7r/nBRmnTTbInajZm0iF1FLjXaFJGzM/XJN4jZiZUtipwaNu66cSwbEhNVUWwZufHjE7SNDUQze3/ciGP9c%2bTampSUS23u%2bcuKrCbj8jPhFIj2Tbritf83YcVaXLJHpEYVz2a1GKHm8/TPot2MgcjD3yBOBd5b/QBImASEcdOpouiYIshi1Ddy20iYL1Jv6JRpPExeWd8q9sEfk4a%2btMHIPFJ%2bdy0e6X9nRMjlx%2bHP0FhUrOp4rADZgAACD41pKeivbukAAIVEy964MrcJsT6MwfHHf54bi9Nfhek/vARUG32qt1HId/3GgYDKcXblAk6I7qW%2boywDMa%2bck59pJms7/pEGqSkLtY%2b5M86%2bWvSG9bNAJWfQnKT1re5L4AxpbJ2J7JOw9md%2byBnt9I3hk%2bQGoY4ZoSMTtZ2QOxIH0rfgxVqrebUjAcCf1AIl7yBusgF1zJITPTBX1fLaEw48VjXjNygQq/N82%2baKZhU2ZMBTtUzgnod4SMrb/IOaQsDF6prVTihGHae6rbRL/Ul4C/au59NAijEBB9evjM6PxSMMpMmag6VTXdVeLeCBFitFwcjtRUH38yIuhPGalBnRTVHyLpm8cS06mrQM2n5j9XnMFBtQzEQEtdvtOsUkIgpVYmqZE38CEh9YAjJBxG1Xo9Pdz/DnHflfc3PWtO1xMZsP/krBMQFxQRTPBOiNv%2bH2DPF1TED35iW%2bS5VDbxjSur1dCVAPqv3Vbduia1moJ1AUYPm9P7%2bcY%2bIV3skuz4Dzk0zQPgU3h7N6zPwr0oi%2bNe548sk6Cqq/wbF7oV6ytuAc0fbedf49I7QA8x8BEruePwNWj2N6v2vJQqQm6fOC%2by7ZQSsJ9830hD6E5yp4YVht%2bux%2b8wu%2bDQ9x/VB6BXLNC%2b4Q2aIhDQmui/JMrgHXgo2okv9FywwVQdaSLXVasfrCVmqzDwGFhIC
&token_type=bearer
&expires_in=3600
&scope=wl.signin%20wl.emails
&user_id=AAAAAAAAAAAAAAAAAAAAAB1EauoOQMnYy_bu42gkx90
&state=STATE
and I parsed that into name value pairs applying decodeURIcomponent to each.
That gave me an access_token value of
EwAwA61DBAAUGCCXc8wU/zFu9QnLdZXy+YnElFkAAQcQQB3c7oVYQmQhyeTOiw1Tp5iA7cjrLJbEnvXVoDlu48mjv7WX35RRIK3L3miAQEUrn5ZVNRSUV8dqiZi4kIko93k2bODqpIY7r/nBRmnTTbInajZm0iF1FLjXaFJGzM/XJN4jZiZUtipwaNu66cSwbEhNVUWwZufHjE7SNDUQze3/ciGP9c+TampSUS23u+cuKrCbj8jPhFIj2Tbritf83YcVaXLJHpEYVz2a1GKHm8/TPot2MgcjD3yBOBd5b/QBImASEcdOpouiYIshi1Ddy20iYL1Jv6JRpPExeWd8q9sEfk4a+tMHIPFJ+dy0e6X9nRMjlx+HP0FhUrOp4rADZgAACD41pKeivbukAAIVEy964MrcJsT6MwfHHf54bi9Nfhek/vARUG32qt1HId/3GgYDKcXblAk6I7qW+oywDMa+ck59pJms7/pEGqSkLtY+5M86+WvSG9bNAJWfQnKT1re5L4AxpbJ2J7JOw9md+yBnt9I3hk+QGoY4ZoSMTtZ2QOxIH0rfgxVqrebUjAcCf1AIl7yBusgF1zJITPTBX1fLaEw48VjXjNygQq/N82+aKZhU2ZMBTtUzgnod4SMrb/IOaQsDF6prVTihGHae6rbRL/Ul4C/au59NAijEBB9evjM6PxSMMpMmag6VTXdVeLeCBFitFwcjtRUH38yIuhPGalBnRTVHyLpm8cS06mrQM2n5j9XnMFBtQzEQEtdvtOsUkIgpVYmqZE38CEh9YAjJBxG1Xo9Pdz/DnHflfc3PWtO1xMZsP/krBMQFxQRTPBOiNv+H2DPF1TED35iW+S5VDbxjSur1dCVAPqv3Vbduia1moJ1AUYPm9P7+cY+IV3skuz4Dzk0zQPgU3h7N6zPwr0oi+Ne548sk6Cqq/wbF7oV6ytuAc0fbedf49I7QA8x8BEruePwNWj2N6v2vJQqQm6fOC+y7ZQSsJ9830hD6E5yp4YVht+ux+8wu+DQ9x/VB6BXLNC+4Q2aIhDQmui/JMrgHXgo2okv9FywwVQdaSLXVasfrCVmqzDwGFhIC
As I understood it, at this point all I have to do is add a header Authorization: Bearertoken value but doing so for a route protected by `[Authorization] produces 401 Unauth with a type of Basic. This leaves me at a bit of a loss as to how to proceed.
Looking at the browser debug info I see
WWW-Authenticate: Bearer error=invalid_token
Does the token being invalid mean this is an inappropriate token or that it can't be parsed as a token?
Is my methodology so far correct?
Is there some kind of server configuration I need to make?
Suggested next steps?
The Access token looks a bit like Base64 but it's not valid base64, I checked. Just thinking about the nature of a token, it's probably just the claims encrypted with the token issuer's private key. If that were so you could check the integrity of a token by decrypting the token using the issuer's public key, but I can't see how to use this observation to figure out why I have a 401.
Update
This is the startup.cs code that introduces token checking
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
SaveToken = true,
TokenValidationParameters = tokenValidationParameters
});
and this auth0 web page cannot decode my token as a JWT token. Further reading suggests that a JWT token has three dot-separated sections each base64 encoded, and this is simply not present in the tokens I'm receiving from both Windows Live and Google (I've tested with both now).
So perhaps I need to restructure the token request if I want JWT tokens. More on this next episode. Feel free to write the next episode as an answer, if you know what I ought to be doing, endpoint and parameters etc. This other question What is the difference between id_token and access_token in Auth0 appears to be salient.
An access_token is what is says on the box - a token for access to a particular resource.
A JWT is an identity token. To get one of those, what I need to do is
use this endpoint: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
specify a response_type of id_token
Include a nonce
Specify a scope of openid.
But returning to the question of how to use an access token, you use it as a bearer token in the authorization header in a requestion to the provider's endpoint for the scope in question, eg wl.email
Related
The access token and refresh token being in the header makes sense to me (from this answer), but I'm wondering the same thing for ID or Identity Tokens. Should those be:
Passed into the body of a post request?
Passed into some header (Authorization header is used by the access token)
Something else (I'm misunderstanding the purpose of the identity token)
A little more context, through SSO, I would like to take the claims of a user (present only in the Identity Token) and sign them up in my system. I can confirm I can do this easily by throwing the identity token into the body of the initial POST request (and validate the signature on the server), but wanted to check if I'm breaking some standard.
Thanks!
In principle you can pass the ID token assuming that:
the software that consumes and validates the ID token (Relying Party, RP) is part of the same application and security domain as the target system
the link between the RP and the target system is secure i.e. authentication of both parties and confidentiality of the communication is established
the target system indeed requires all of the information in the ID token, otherwise it would be better to apply minimal disclosure and pass only a subset of the information
Passing the ID token (or the subset of claims) in headers may work if the amount of data is relatively small, otherwise you may run into HTTP header length limitations. Using POST though has the downside of having to modify the application request in flight which may be harder and have side effects.
For completeness, the refresh token is never passed in a header and only sent to the Authorization Server / Provider by the RP in a POST request.
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.
I'm taking a ASP.NET security course on OpenID Connect authorization code workflow with PKCE protection against code replay attack. There is one aspect of this process that I don't understand.
The process:
Relying client generates PKCE code_verifier, hash it into code_challenge, and send the user to the authorization server with the code_challenge as a query parameter.
The authorization server stores the code_challenge, issues authorization code, and redirects user back to client with the authorization code.
The client sends the authorization code along with the original code_verifier to exchange for tokens. The authorization server verifies that the code_verifier indeed hashes into code_challenge before issuing the token.
My question is step #3: since HTTP is stateless, how does the client know which code_verifier to send along with the authorization code? Is this code_verifier stored in a cookie on the user agent?
Using a cookie to store the "code verifier" appears to violate the spirit of the OpenId specification and just appears to be a big security hole.
In brief, the spec expects you to use S256 to hash the code verifier and the code verifier to be kept secret from eavesdropping or being guessed.
PKCE RFC Section 7.1 says the "The security model relies on the fact that the code verifier is not learned or guessed by the attacker. It is vitally important to adhere to this principle. As such, the code verifier has to be created in such a manner that it is cryptographically random and has high entropy that it is not practical for the attacker to guess."
... "The use of "S256" protects against disclosure of the "code_verifier" value to an attacker."
Now it does give an opening to using a cookie if you encrypt the "code verifier". Here is the what the spec says in
Section 7.2: "If the code challenge method is "plain" and the code challenge is to be returned inside authorization "code" to achieve a stateless server, it MUST be encrypted in such a manner that only the server can decrypt and extract it."
Well, OpenId specification has a solution and Microsoft has provided an implementation to follow. Refer to the "state" parameter in the OpenId specification when calling the "authorization" endpoint. The Identity Providers will return the value back to the caller.
The way it is done in .NET Core is using the "state" query string parameter whereby the value is a dictionary but protected (encrypted). For help look through the .NET core code and look at the method "BuildChallengeUrl" in "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler"
Also, this Site has a great idea that works. It's a valid option.
PKCE was originally intended for mobile clients, which typically make only a single authorization request. But it is now recommended for all clients using the authorization code flow.
You're correct that if the client is a web application serving many users, the client application needs to associate the correct code_verifier with the authorization request.
As you say, the client could store the code_verifier in a cookie, so it will be sent to the client together with the authorization code.
Or it could store it server side on the client and stick the key in the state parameter to the authorization request. The state will be part of the call to the redirect_uri if passed along with the authorization request.
What's the reasoning behind client_secret being provided as URL parameter when exchanging code for access_token? URL params have their weakness, such as often being stored in logs. Considering the exchange is done via the POST method, why is the secret not passed in the payload instead?
In most cases the secret is passed in the header and different implementations of OAuth have either a sha hashing to secure the request. Is there a specific provider that you are referring to?
While learning how OAuth2 works, I cannot figure out why there is a separate step to retrieve Access Token?
A separate step means:
an extract HTTP request
passing Client Secret in the URL
I'd expect the Access Token to be generated in the "authorization" step, encrypted with using the Client Secret, and returned back when redirecting to the Callback URL. Then the client application would decrypt it and use it straight await without issuing an extra HTTP request.
I guess there are some reasons behind having an extra step, and I'm just not aware of them. I hope you can explain the reasons in your answer.
I'm assuming you're talking about the Authorization Code flow and not the Implicit flow, which does return a token directly.
The Authorization Code flow is designed to work with potentially unencrypted servers via a callback URL (this was designed years before Let's Encrypt and the relatively recent encrypt-everything push). Thus, the URL could be intercepted by any intermediate routers/proxies, and sending an access code as part of the callback URL in that environment is a Bad Idea.
So instead, the authorization code is sent. Then the client exchanges the authorization code along with its client secret for an access token. The authorization code can be intercepted, but in the Authorization Code flow, the client secret is actually secret and not known outside your server, so any intercepted authorization code is useless on its own.
These days, encryption is common and free, and unencrypted flows are strongly discouraged. The extra call remains part of the Authorization Code flow for historical reasons.