Generate SWT token, and sign it, from IClaimsPrincipal - claims-based-identity

I would like to create a SWT token based on the claims in:
IClaimsPrincipal principal = (IClaimsPrincipal)User;
IClaimsIdentity identity = (IClaimsIdentity)principal.Identity;
I have found code for validating a swt token, but haven't been able to find a sample that creates one from IClaimsIdentity claims.

Check out my blog it that is of any help to you... http://lbadri.wordpress.com/2012/07/30/anatomy-of-a-simple-web-token-swt/

Related

API Authentication using multiple Authentication Providers

This topic is one that feels like it should be documented better - or perhaps I am using the wrong terms when searching.
I have several SPA apps that use various Oauth2 logins
(ie. Okta, Facebook, Google) to authenticate and generate access
tokens.
These apps all access a common API backend (asp.net core). All
requests to the API have the Oauth2 access token attached as an Authorization header.
How do I configure this single backend API to validate these access tokens from one of a variety of providers, without knowing in advance which access token is attached, and decode a user email address that I can use for further authorization purposes?
I have found much documentation on validating tokens from a descrete, known authorization provider, but very little on using multiple providers. With all the apps out there that give you a choice of Oauth2 logons to choose from (StackOverflow among them), I thought this would be a more common problem.
What am I missing!?
It seems like the correct way to address this situation is to build a Custom Authentication Handler as documented here: https://referbruv.com/blog/posts/implementing-custom-authentication-scheme-and-handler-in-aspnet-core-3x
In this Authentication Handler I can decode the token, assert that the issuer is a member of a whitelist, validate the access token using the issuer's public key, and use the rest of the token to build the Identity I need for further authorization.
At least now I have a better idea what to search for, and I'm not completely re-inventing the authentication mechanism!
You will want to identify the user in a consistent way in your APIs, then authorize requests based on the identity + scopes.
This will be very difficult when using many different token providers, as you are finding. Their access tokens are not designed for you to use in your own APIs.
A better mechanism is to use tokens only from your own Authorization Server, to support different login methods but also put your code in control. My Federated Logins blog post has further info.
It turns out I was overthinking this after all.
Since I am dealing with an API backend, all I needed to do was to validate IDP Bearer tokens, not to create them. In the end, I was able to validate 3 ID providers using the folowing simple code:
services.AddAuthentication(OKTA_SCHEME)
.AddJwtBearer(ADFS_SCHEME, options =>
{
options.Authority = adfsConfig.authority;
options.Authority = adfsConfig.authority;
})
.AddJwtBearer(GOOGLE_SCHEME, jwt => jwt.UseGoogle(
clientId: googleConfig.clientId
))
.AddJwtBearer(OKTA_SCHEME, options =>
{
options.Authority = oktaConfig.authority;
options.Audience = oktaConfig.audience;
});
Note that this required the installation of one additional nuget package to simplify the validation of the Google tokens, which don't appear to follow the standard: Hellang.Authentication.JwtBearer.Google.
At this point I can authorize using attributes like:
[Authorize(AuthorizationSchemes = OKTA_SCHEME)]
...or set up policies based on the schemes.
The second part problem was to link my various logons to users in a local database, which I ended up doing using a custom IClaimsTransformation that uses the information populated to ClaimsPrincipal to lookup a the user in my database, and add an "Employee" role claim, if they are found.
public class EmployeeClaims : IClaimsTransformation
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (!principal.HasClaim(a => a.Type == "EmployeeNumber"))
{
Employee employee = lookupEmployee(principal);
if (employee != null)
{
ClaimsIdentity id = new ClaimsIdentity();
id.AddClaim(new Claim(ClaimTypes.Role, "Employee"));
id.AddClaim(new Claim("EmployeeNumber", employee.EmployeeNumber.ToString()));
principal.AddIdentity(id);
}
}
return Task.FromResult(principal);
}
private Employee lookupEmployee(ClaimsPrincipal principal) {
string issuer = principal.Claims.Single(a => a.Type == "iss").Value;
if (issuer.Contains("google.com"))
...
}
}
This IClaimsTransformation is then registered by:
services.AddScoped<IClaimsTransformation, EmployeeClaims>();
Now I can additionally authorize employees with:
[Authorize(Roles = "Employee")]

Get "groups" claims from Okta using the OpenID Connect Authorization Code Flow

I'm trying to include "groups" claims in what is returned by Okta after a user authenticates. It returns them when the response_type is 'id_token' but not when response_type is 'code'. For the Authorization Code flow I would expect to get the groups claims from the userinfo endpoint but they're not there.
However I've read that the authorization code flow is more secure than the hybrid flow (id_token) so I'd like to ensure there is not a way to do this?
My webapp is built on ASPNET Core 3 and I've tried the Okta.AspNetCore Nuget package.
One thing that might trip you up is that Okta do return the tokens you ask for, but the OpenIDConnect handler in your client blocks them.
You need to explicitly map those extra claims in your client, using code like:
options.ClaimActions.MapUniqueJsonKey("website", "website");
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
options.ClaimActions.MapUniqueJsonKey("birthdate", "birthdate");
There is also this option you can set:
options.GetClaimsFromUserInfoEndpoint = true;
Do verify using tools like Fiddler the the claims actually is returned or not.
And yes, authorization code flow is what you should aim to use.
/userinfo response should contain all claims (for all flows including authorization code flow) including 'groups' as long as the groups scope is sent in the requests to mint the token.
Could you make sure the user is part of this group and the right scope is passed in the request ?
You can easily add 'groups' claim in access token as well. You can refer to the guide below:
https://developer.okta.com/docs/guides/customize-tokens-groups-claim/overview/

Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential

I am trying to update Google sheet values.
"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential."
I want to do this using API key not outh 2.0
Can anyone have any suggestions.
Not possible. You need to use the OAuth login as indicated here in spreadsheets.values.batchUpdate:
You can see on the authorization part that it uses OAuth scopes, therefore it follows that it uses OAuth not API KEY:
Authorization
Requires one of the following OAuth scopes:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/spreadsheets
I am facing similar type of issue.
Issue Analysis:
Actual issue is missing of scope.
Solution:
Scope can be added by the following ways:
using enabling api
Adding oauth scope list. List is available here: https://developers.google.com/identity/protocols/googlescopes
Adding scope from Code level
Details are available here:
If any scope is missing, first we need to check that related api is enabled or not. If not enabled yet, go first on google console and enable api.
Procedure is available here: https://support.google.com/googleapi/answer/6158841?hl=en
After enabling api, sometimes you will not get your expected scope. If there is any sophisticated scope is required, you need to add those in your scope list. For this, you need to follow the procedure:
i) Go to google console
ii) Select your project
iii) In left sidebar, you will get "Oauth consent screen". Click on that button
iv) You will get "Add scope" button. There is actually 3 is enlisted primarily: email, profile and openID
v) You can add your expected scope here.
In code level, we can also add SCOPES. In code level, I am using singleton list. Which is always using single scope. So when I need another scopes like user profile or user email. I can't reach those. So I changed it and got my expected result.
Previous Code:
private static final List<String> SCOPES = Collections.singletonList(DriveScopes.DRIVE);
After change:
private static final List<String> SCOPES = new ArrayList<>(
Arrays.asList(DriveScopes.DRIVE,
Oauth2Scopes.USERINFO_EMAIL,
Oauth2Scopes.USERINFO_PROFILE,
Oauth2Scopes.PLUS_ME));

Should Name Claim remain omitted in JWT Access Token with IdentityServer4 (using Identity) and ASP.Net Core API?

While configuring my IdentityServer4 (using Identity) resource owner grant flow with an asp.net core API backend, I got to thinking that perhaps the "Name" claim should remain omitted in the JWT access token for user security? This claim is not available with out of the box behavior of IS4.
Previously, I had been adding in the "Name" claim for the access token in my IS4 Config.cs file as follows:
var claims = new List<string>
{
JwtClaimTypes.Name
};
return new List<ApiResource>
{
new ApiResource("api1", "Auth API", claims)
};
I was doing this because it allows a straightforward approach to get a logged in user's ClaimsPrincipal.Identity.Name for user look up inside a Controller action.
var name = User.Identity.Name;
var user = await _userManager.FindByNameAsync(name);
However, IS4 access tokens (when using Identity) include the user's GUID id in the "Sub" claim. With this, we can also look up a user using the following:
var userId = User.Claims.FirstOrDefault(u => u.Type == "sub").Value;
var user = await _userManager.FindByIdAsync(userId);
I know there is slightly more processing with the LINQ query (hardly anything tbh), but I was thinking it might be of worth to protect a user's username (email address in my situation) if an access token ever fell into the wrong hands. Especially since JWT's are so easy to decode with the likes of jwt.io.
Do you guys agree or disagree? Or am I looking at this the wrong way and missing something?
JWT usually contain the public data and it is self-contained. i.e. You don't need to communicate with a backend server to construct user's identity. You should prevent the token fell into wrong hand by using https. Also, you should balance your token validity window(usability vs security) and use a nonce for maximizing the security.
I don't think 'name' should be omitted from claim collection. A valid use-case for what you are doing is that you need to make sure that changes to your user store immediately reflect in your web API. In the case of a self-contained token, if you change the 'name' in the data store, the user will not see that change until he was issued a new token. In this case use of a 'reference token' might be a good option.
Also, It looks like you are directly accessing user store from the web API. While you might have valid reasoning behind this, Idea of using token based authentication is to delegate authentication to external party(Identity Server). So common pattern is to
Include every public data that you require in the web API in the
access token.
If token getting too big, include a subset of claims in the token and query user info endpoint when required.
Use reference tokens if you have valid reasons to do so. But this will affect the performance as it will require back channel communication with identity server.

How to identify a Google OAuth2 user?

I used Facebook login to identify users. When a new user comes, I store their userID in my database. Next time they come, I recognized their Facebook ID and I know which user it is in my database.
Now I am trying to do the same with Google's OAuth2, but how can I recognize the users?
Google sends me several codes and tokens (access_token, id_token, refresh_token), however none of them are constant. Meaning if I log out and log back in 2 minutes later, all 3 values have changed. How can I uniquely identify the user?
I am using their PHP client library: https://code.google.com/p/google-api-php-client/
As others have mentioned, you can send a GET to https://www.googleapis.com/oauth2/v3/userinfo, using the OAuth2 bearer token you just received, and you will get a response with some information about the user (id, name, etc.).
It's also worth mentioning that Google implements OpenID Connect and that this user info endpoint is just one part of it.
OpenID Connect is an authentication layer on top of OAuth2. When exchanging a authorization code at Google's token endpoint, you get an access token (the access_token parameter) as well as an OpenID Connect ID token (the id_token parameter).
Both these tokens are JWT (JSON Web Token, https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token).
If you decode them, you'll get some assertions, including the id of the user. If you link this ID to a user in your DB, you can immediately identify them without having to do an extra userinfo GET (saves time).
As mentioned in the comments, these tokens are signed with Google's private key and you may want to verify the signature using Google's public key (https://www.googleapis.com/oauth2/v3/certs) to make sure they are authentic.
You can see what's in a JWT by pasting it at https://jwt.io/ (scroll down for the JWT debugger). The assertions look something like:
{
"iss":"accounts.google.com",
"id":"1625346125341653",
"cid":"8932346534566-hoaf42fgdfgie1lm5nnl5675g7f167ovk8.apps.googleusercontent.com",
"aud":"8932346534566-hoaf42fgdfgie1lm5nnl5675g7f167ovk8.apps.googleusercontent.com",
"token_hash":"WQfLjdG1mDJHgJutmkjhKDCdA",
"iat":1567923785,
"exp":1350926995
}
There are also libraries for various programming languages to programatically decode JWTs.
PS: to get an up to date list of URLs and features supported by Google's OpenID Connect provider you can check that URL: https://accounts.google.com/.well-known/openid-configuration.
I inserted this method into google-api-php-client/src/apiClient.php:
public function getUserInfo()
{
$req = new apiHttpRequest('https://www.googleapis.com/oauth2/v1/userinfo');
// XXX error handling missing, this is just a rough draft
$req = $this->auth->sign($req);
$resp = $this->io->makeRequest($req)->getResponseBody();
return json_decode($resp, 1);
}
Now I can call:
$client->setAccessToken($_SESSION[ 'token' ]);
$userinfo = $client->getUserInfo();
It returns an array like this (plus e-mail if that scope has been requested):
Array
(
[id] => 1045636599999999999
[name] => Tim Strehle
[given_name] => Tim
[family_name] => Strehle
[locale] => de
)
The solution originated from this thread: https://groups.google.com/forum/#!msg/google-api-php-client/o1BRsQ9NvUQ/xa532MxegFIJ
It should be mentioned, that the OpenID Connect API returns no id attribute anymore.
It's now the sub attribute which serves as a unique user identification.
See Google Dev OpenID Connect UserInfo
"Who is this?" is essentially a service; you have to request access to it as a scope and then make a request to the Google profile resource server to get the identity. See OAuth 2.0 for Login for the details.
Altough JWTs can be validated locally with the public key, (Google APIs Client Library downloads and caches they public keys automatically) checking the token on Google's side via the https://www.googleapis.com/oauth2/v1/tokeninfo endpoint is necessary to check if the access for the applicaton has been revoked since the creation of the token.
Java version
OAuth2Sample.java