Update bearer token without logging out and in again using WIF - 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.

Related

Obtaining user's email using Apple's IdentityToken

On my mobile app I use apple-id authorization. My implementation uses ASAuthorizationAppleIdProvider class and does not require additional proxy web-application, that sends request to Apple. All interaction is done on the mobile client.
All things work ok, and I get authorized and I get IdentityToken from Apple.
Now, I want to send this IdentityToken (looks like "AjzN91mNajN3401jazOs001m3ks") to the server. And on the server side I want to extract user's email from this token.
For Google to solve the same task I have to send GET request with token, like that
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=google_token
and if token is valid in response I get JSON with user's email inside.
How can I do the same for the Apple using Apple's identity key?
Update #1:
My project has 2 parts, client part (frontend) and server part (backend).
The functionality to obtain IdentityToken looks like that (AuthManager is just a delegate):
var provider = new ASAuthorizationAppleIdProvider();
var req = provider.CreateRequest();
authManager = new AuthManager(UIApplication.SharedApplication.KeyWindow);
req.RequestedScopes = new[] {
ASAuthorizationScope.FullName,
ASAuthorizationScope.Email
};
var controller = new ASAuthorizationController(new[] {
req
}) {
Delegate = authManager,
PresentationContextProvider = authManager
};
controller.PerformRequests();
ASAuthorizationAppleIdCredential credentials = await authManager.Credentials;
When I get credentials, there is credentials.IdentityToken property available.
Now, I want to send this identity token to the server, to let the server check this token and obtain user's email using this token from the Apple server, like I do for Google (described above).
And I do not understand, how can I do that.
What Apple endpoint and what HTTP request (GET, POST) should be used to achieve this task?
In OpenID Connect, the Identity Token is never sent to the Provider. I think this is just a typo/naming issue and you mean the Access Token.
The end result of the user authenticating is two tokens:
the Access Token, an opaque token which is not meant to be introspected by the Client. It may or may not be a JWT.
an ID Token, a JWT which contains the user claims.
To obtain the user's email address, decode the ID/Identity Token's JWT payload. To do this in Swift, see these SO answers. The JWT should contain an email value. It looks like the email address may also be an instance property of ASAuthorizationAppleIdProvider, so you should be able to get them from credentials.email.
There does not appear to be a way to directly validate the Access Token. Most OpenID Connect Providers offer a Userinfo Endpoint, or a Token Instrospection Endpoint (I think this is the Google endpoint that was linked in question), but Apple does not. A number of steps were already performed to obtain the Access Token, however, which should make it impossible to forge. If you really only want the email address, though, JWTs are cryptographically signed, so verifying the JWT should guarantee it was issued by Apple. You can also verify the Refresh Token as shown in Apple Developer docs. In your code above, I don't see a way to access Refresh Token, but if you followed an alternative flow as shown in one of the tutorials here or here, you would be able to.

Sign In With Apple refresh token validation only access token returned

I'm using the AppleAuth npm package in my server to submit token requests to Apple's servers during the sign in with Apple process. (This is based off the sample server code provided with the sign_in_with_apple package in pub.dev) I have no issues submitting the authorization code to get my first access and refresh tokens.
However, when trying to test how my app would get a new refresh token, when I submit a POST request to https://appleid.apple.com/auth/token with the grant_type set to refresh_token the response I get is different than from having the grant_type set to authorization_code.
I looked at the source code of the package, and for its AppleAuth(myConfig).refreshToken(myRefreshToken) (<- pseudo code lol) the only difference in the POST payload is:
{
grant_type: 'refresh_token', // instead of grant_type: 'authorization_code'
refresh_token: refreshToken, // instead of code: authorizationCode
... // other params
}
While the initial request with authorization code returns both an access token and a refresh token, for some reason the refresh token request is only returning the access token. I really don't think it's the package causing the error, nor can I see how my code would be the source either, since the above code is the only difference.
I tried passing the access token that it returns in a new refresh token request in order to test that I can get new refresh tokens whenever necessary in the future, but it returns a 400 status error.
Am I missing something here? Is there a reason the refresh token request returns no new refresh token? Or am I missing something entirely about how the process/flow is supposed to work? I am trying to do this for the "check the refresh token once daily to confirm the user is still in good standing with Apple's servers" part of the process.
I've really been stuck on what to do at this point. I can save the identity_token.sub field in my database to check whether my user is signed in, but of course I want to make sure my user's apple ID is still valid with apple, and that they haven't revoked access. Could it be that because I tried to get a new refresh_token too soon Apple only returned the access_token?
Oh also, the app itself is a Flutter app and I am testing all of this on my iPhone 11 so it's not an Android/Web flow.
Whether you get a new 'rolling / rotating' refresh token in a refresh token grant response is generally vendor specific:
You may get a new refresh token occasionally but not always
The primary purpose of this message is to get a new access token, not a new refresh token
In terms of token handling, the client should update its tokens similarly to the saveTokens method in this sample of mine.
I've not used Sign In with Apple but I suspect proceeding as follows would be the simplest solution:
Keep access tokens short lived: no more than 60 minutes
This forces a frequent token refresh, which by default is very quick
If the user's Apple Id is revoked I would expect this to return an invalid_grant response

Use an ID token or access token at userinfo endpoint?

I have a client API, that is a confidential client. When I authenticate with an open id provider, I am redirected to my callback with an authorization code, which is immediately exchanged to receive a refresh token, an access token, and an ID token.
Now, I create a session cookie that has a uuid for the authenticated user. When the user makes a request, do I...
Use my access token to call the providers userinfo endpoint to get the user info.
Read the validated ID token to get the users info.
When it comes to using the refresh token I see 2 options:
After reading a valid ID token or access token during a request, use the refresh token to get a new access or ID token to store at a new uuid, which is returned to the user with an updated cookie. While requiring the user to sign in more, this means the users session becomes invalid after inactivity on their part equaling the lifetime of the access or ID token. This is potentially more secure.
Use the ID token or access token until valid and then refresh to get a new one. If the refresh never expires, the user will never have to sign in again even if inactive for a long period of time ( unless cookie expiration is low ) Potentially less secure.
Thoughts?
A few notes first:
the lifetime of the application session is (typically) independent of the lifetime of the ID token; the latter is just an assertion about the user's identity, it doesn't represent a session
your first option doesn't work with a parallel requests e.g. when a user has opened multiple tabs to your application or the application uses Javascript calls
But foremost: a refresh token should not be used to get a new ID token, it should only refresh the access token; a user needs to be present to get a new ID token with the same semantics as the original one.
In short, you only use an authentication token to access userinfo_endpoint uri.
OpenID Connect allows the use of a "Discovery document," a JSON document found at a well-known location containing key-value pairs which provide details about the OpenID Connect provider's configuration, including the URIs of the authorization, token, revocation, userinfo, and public-keys endpoints.
You can research each applications unique discovery page uri from their docs for example here is
Google
You make a get request to the discovery document uri and from this document you find the userinfo_endpoint uri.
Example response from microsoft
GET https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration
{
"authorization_endpoint": "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize",
"token_endpoint": "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token",
"token_endpoint_auth_methods_supported": [
"client_secret_post",
"private_key_jwt"
],
"jwks_uri": "https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys",
"userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo",
"subject_types_supported": [
"pairwise"
],
...
}
Google's discovery doc uri
GET https://accounts.google.com/.well-known/openid-configuration
Get an Authorization token. For example pull up Network -> Fetch/ XHR now look around and try to find a request header with the key 'authorization'. Copy 'Bearer {the id}' and put in the header of a get request like the picture shown below.
GET or POST /oidc/userinfo HTTP/1.1
Host: graph.microsoft.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6Il…
Microsoft Example Postman Request

OAuth 2.0 for MVC - How does the RequestToken work?

I'm working with OAuth 2.0 for MVC, found here: http://community.codesmithtools.com/CodeSmith_Community/b/tdupont/archive/2011/03/18/oauth-2-0-for-mvc-two-legged-implementation.aspx
For anyone who's worked with this - I'm confused about the RequestToken. There is a controller implemented that lets you get a request token, which expires in 5 minutes, and you pass that token back in to get an AccessToken. But it never checks the request token for validity - it seems like you can pass in any access token you want to. What is the idea for the RequestToken here - are you supposed to create your own method of storing, referencing, and then deleting that token for those 5 minutes?
Thanks,
Andy
This is all about how OAuth works in conjunction with your application Id, application secret key and valid domains for your application. Here is the process in general
Your application sends a request to the OAuth provider using your application Id and secret along with a callback (return Url).
The OAuth provider gets the request, checks your application Id and secret and validates that the callback url is from a domain that you have specified for your application.
2a. If the callback url is not from a domain that you have specified, then the request is rejected with error.
2b If the callback url is from your domain, it returns a temporary request key to your server.
Given that you received a request key, you send that back to the OAuth provider to get the actual access token for the user.
Now, as to why the request key step is in place, this is to prevent and help protect 'bad people' from attempting to use your application id to falsely authenticate other users. By sending the request token to you (a callback URL that you have approved), the OAuth provider has confidence that the request actually came from your servers.
You most certainly could send any string back instead of the request token, but you would quickly get an error back from the OAuth provider as that request token does not correspond to any existing authentication request from any known application.
Lastly, I am not clear on what you mean by 'validating the request token'? You did not generate the token not probably do not have insight into the algorithm to generate the request token. Given that, I am not sure how you would validate this. If you are concerned about validating the first step, take a look at the Facebook OAuth process. In there, they recommend sending a request key as part of your return Url(as a query string parameter). That request key will come back to your application which you could then use as a validation that, indeed, this is a response to a request that you made. How you store and track that request key is up to you (session, database). In the PHP samples, they use a 'state' variable to track a unique/arbitrary string: Facebook OAuth Server Side Login Example (in PHP)

Client Credential Grant fails on AuthorizeRequest due to lack of Refresh Token

A client credential grant does not return a refresh token (DotNetOpenAuth.OAuth2.AuthorizationServer.PrepareAccessTokenRequest forbids it). But ClientBase.AuthorizeRequest requires it.
Is this a bug in DotNetOpenAuth or am I doing something wrong?
I suppose I can work around by inheriting ClientBase and overriding AuthorizeRequest. Is that the correct thing to do?
Edit: It's not so easy to inherit from ClientBase outside of DotNetOpenAuth because a lot of the stuff you want is internal only. e.g. ErrorUtilities.VerifyProtocol
Edit2: Just read the draft OAuth 2 spec (draft 25) referred to in DotNetOpenAuth.OAuth2.AuthorizationServer.PrepareAccessTokenRequest and I can't find where it disallows refresh tokens for Client credential grant type. Maybe they changed it?
Google returns Refresh Token if you request it. Provide parameter in query string access_type=offline.
In my case I had to amend default Authorization Endpoint URL to: https://accounts.google.com/o/oauth2/auth?access_type=offline
Google Api C# example using DotNetOpenAuth:
private WebServerClient GetClient()
{
return new WebServerClient(
new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth?access_type=offline"),
TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"),
ProtocolVersion = ProtocolVersion.V20,
},
clientIdentifier: this.settings.GoogleApisClientIdentifier,
clientSecret: this.settings.GoogleApisClientSecret
);
}
NOTE from my experience: This works only for the First request.
See Google Documentation.
I'm not sure why you say that ClientBase.AuthorizeRequest requires it. Firstly, there is an overload that only takes an access token, so it doesn't even ask for a refresh token. The overload you may have tried accepts an IAuthorizationState object, which may or may not include a refresh token, and it appears that that method only looks for a refresh token if the access token has expired. Since an expired access token can't be used, it tries to refresh it and throws if it can't. It seems reasonable to me.
Whichever method overload you choose to call, your calling mode must either avoid using expired access tokens or be prepared to respond to the exceptions that are thrown when DotNetOpenAuth or the resource server determines that they are expired or revoked. In fact since tokens can be revoked before they expire, it's a good idea to always be prepared for that.
The OAuth 2 spec draft 25 does in fact indicate that a refresh token should not be included in a response to the client credentials grant. From section 4.4.3:
4.4.3. Access Token Response
If the access token request is valid and authorized, the authorization server issues an access token as described in Section 5.1. A refresh token SHOULD NOT be included. If the request failed client authentication or is invalid, the authorization server returns an error response as described in Section 5.2.

Resources