IdentityServer, how to validate token using multiple authorities - access-token

How can I use IdentityServer.accesstokenvalidation package to validate tokens using multiple authorities?
In my front end application I am getting a token using let us say on of the following:
1- subdomain1.identityserver.com
2- subdomain2.identityserver.com
3- subdomain3.identityserver.com
Now if I get a token using subdomain1.identityserver.com then the token will look like:
{
"nbf": ,
"exp": ,
"iss": "subdomain1.identityserver.com",
"aud": [
"subdomain1.identityserver.com/resources",
],
"client_id": "Frontend",
"sub": "",
"auth_time": 1516002171,
"idp": "local",
"scope": [
"openid",
"profile",
],
"amr": [
"external"
]
}
In my APIs I am using IdentityServer.accesstokenvalidation to validate these tokens, how can I tell my APIs to use the issuer (iss in token) as authority?
I tried something like:
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = variable,
DelayLoadMetadata = true,
});
Where Authority is a variable, but it looks like the authority would be registered once at startup.

Related

Decoding jwt token returned by the new google identity api

I have created a login button with google identity javascript api https://developers.google.com/identity/gsi/web/guides/display-button#javascript
I have successfully returned a jwt token and decoded it successfully. I have a field in the jwt token called kid and the docs say this is its purpose
the thumbprint for the public key used to verify this token
Will the kid field always be the same for the email i logged in with and can it ever change?
I am looking to extract a field from the jwt that will never change so that i can track users who have logged in on web and on android apps.
Run the id token through Jwt.io The payload data should look something like this.
{
"iss": "accounts.google.com",
"azp": "1046123799103-q2fg15qscp8apoh8fcf490o3d0bk.apps.googleusercontent.com",
"aud": "1046123799103-q2fg15qscp8apoh8fcf490o3d0bk.apps.googleusercontent.com",
"sub": "117200475532672775346",
"email": "XXXXX#gmail.com",
"email_verified": true,
"at_hash": "LqL3dnsD9w-elE-unya7-g",
"iat": 1662038461,
"exp": 1662042061
}
In this example the sub is the users internal id on google.
If i run it though the token info endpoint
https://oauth2.googleapis.com/tokeninfo?id_token=Id_token
I get the same sub claim
{
"iss": "accounts.google.com",
"azp": "1046123799103-q2fg15qscp8apoh8fcf490o3d0bk.apps.googleusercontent.com",
"aud": "1046123799103-q2fg15qscp8apoh8fcf490o3d0bk.apps.googleusercontent.com",
"sub": "117200475532672775346",
"email": "XXXX#gmail.com",
"email_verified": "true",
"at_hash": "M_28bzozJTabf3e8Q1yyeQ",
"iat": "1662045590",
"exp": "1662049190",
"alg": "RS256",
"kid": "402f305b70581329ff289b5b3a67283806eca893",
"typ": "JWT"
}
Sub claim is normally used for account linking between the internal user system of a site and the external third party logins.
you can use base64url_decode function to decode your jwt like that, I had the similar problem that I've solve like that in symfony 5.3:
$jwt = $request->request->get('credential');
$match=explode('.',$jwt);
function base64url_decode($base64url)
{
$base64 = strtr($base64url, '-_', '+/');
$plainText = base64_decode($base64);
return ($plainText);
}
$payload=base64url_decode($match[1]);
$payloadObject=json_decode($payload);
$verif = $payloadObject->email_verified;
$email = $payloadObject->email;
...etc

django allauth and PKCE

I have to get login credentials from a 3rd service which require a PKCE to authenticate.
I was thinking to use django-allauth to do it but I can't find a way to send the pkce in my request. There is nothing fancy in the way I'm doing it.
I generate a PKCE but where can I add it in the allauth request ?
The configuration is quite simple atm, I did a custom SOCIALACCOUNT_PROVIDERS in my settings.py. It contact the server but the PKCE ( code_challenge ) is missing.
SOCIALACCOUNT_PROVIDERS = {
"auth0": {
"AUTH0_URL": provider,
"APP": {
"client_id": client_id,
"secret": secret
"key": "",
"code_challenge": code_challenge
}
}
}
Any idea ? Thanks
Try adding AUTH_PARAMS
SOCIALACCOUNT_PROVIDERS = {
"auth0": {
"AUTH0_URL": provider,
"APP": {
"client_id": client_id,
"secret": secret
"key": "",
},
'AUTH_PARAMS': {
'code_challenge_method': 'S256',
'code_challenge': '362t6atUC1Fz'
}
}
}

The directline api uses the id and name to authenticate but the name never persists through when a conversation is subscribed to and activity is sent

Here is an example of when I authenticate using directline.
{
"user": {
"id": "string",
"name": "string"
},
"trustedOrigins": [
"string"
]
}
1. user.id string Optional. Channel-specific ID of the user to encode within the token. For a Direct Line user, this must begin with dl_. You can create a unique user ID for each conversation, and for better security, you should make this ID unguessable.
2. user.name string Optional. The display-friendly name of the user to encode within the token.
Now, I would expect that the token I receive has the id and the name inside which it does.
Here is an example of the token response:
{
"bot": "my-bot",
"site": "ddddddd",
"conv": "xxxxxxxxx-j",
"user": "77777777777",
"username": "{\"first\":\"Christian\",\"last\":\"Matthew\"}",
"nbf": 1592789668,
"exp": 1592793268,
"iss": "https://directline.botframework.com/",
"aud": "https://directline.botframework.com/"
}
Now, when I use this token to start a conversation the token works as epected.
The error comes or unexpected return is when I send an activity to that conversation. The from is the correct id userId but the name isn't there.
Is it there in the bot communication? I would like for the name field to be there. Perhaps it is just not posted in the front-end client message return.
Here is an example of the message that is returned.
message received:
{
"activities": [
{
"type":"message",
"id":"DtjXwD1VvG7Eu69LR5ZL31-6|0000002",
"timestamp":"2020-06-22T01:58:16.379228Z",
"serviceUrl":"https://directline.botframework.com/",
"channelId":"directline",
"from": {
"id":"77777777777"
},
"conversation": {
"id":"DtjXwD1VvG7Eu69LR5ZL31-6"
},
"recipient": {
"id":"xxxxx-xxxxx-bot#2MnpO8SotMQ",
"name":"xxxxx-xxxxx-bot"
},
"text":"xxxxxxx xxxxxxx"
}
]
}
The id is all that comes through. Is the userName or name available inside of the bot service? I need to capture that.
UPDATE: Add reference to the directline conversation starter token.
POST https://directline.botframework.com/v3/directline/conversations
Authorization: Bearer SECRET_OR_TOKEN

Link AccessToken to a user in Loopback

I wanted to add a custom attribute (expireAt) to the access token for it to be used by MongoDB to automatically remove the expired access token when expired.
Adding the custom attribute while creating the access token using AccessToken model is working well:
const ttl = 600;
const expireAt = new Date();
expireAt.setSeconds(expireAt.getSeconds() + ttl);
const token = await AccessToken.create({ ttl, expireAt });
However, when I want to create an access token for a user, I can't add the custom attribute exprieAt when creating the token, so I have create first, and then update it:
const ttl = 600;
const expireAt = new Date();
expireAt.setSeconds(expireAt.getSeconds() + ttl);
// Create the access token for the user
const token = await user.createAccessToken(options);
// Update token to set the custom date and time to expire
token.expireAt = expireAt;
token.save();
// Return the token together with the user data
return Object.assign({}, token.toJSON(), { user });
Is there a way I can create the token for a user with the custom attribute (either using instance method or model method is fine), without doing two steps - create and update?
So it seems that AccessToken model has the relationship set up with the user via userId attribute (ref: https://github.com/strongloop/loopback/blob/master/common/models/access-token.json#L27).
{
"name": "AccessToken",
"properties": {
"id": {
"type": "string",
"id": true
},
"ttl": {
"type": "number",
"ttl": true,
"default": 1209600,
"description": "time to live in seconds (2 weeks by default)"
},
"scopes": {
"type": ["string"],
"description": "Array of scopes granted to this access token."
},
"created": {
"type": "Date",
"defaultFn": "now"
}
},
"relations": {
"user": {
"type": "belongsTo",
"model": "User",
"foreignKey": "userId"
}
},
"acls": [
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
}
]
}
To link the token to the user, we only need to pass in the value userId:
AccessToken.create({ ttl, expireAt, userId });

Why is Google's OpenId Connect API not sending back all claims?

I looked at Google's OpenId Connect discovery document here. It clearly shows that the supported Claims are:
"claims_supported": [
"aud",
"email",
"email_verified",
"exp",
"family_name",
"given_name",
"iat",
"iss",
"locale",
"name",
"picture",
"sub"
]
and the supported Scopes are
"scopes_supported": [
"openid",
"email",
"profile"
]
I would expect that when I send a GET request to the OpenId Connect UserInfo end point (which is https://www.googleapis.com/oauth2/v3/userinfo) that I would get back all of the supported claims (assuming that when I authenticated I requested all of the supported Scopes... which I did when I send the initial request as shown below)
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=my-client-id&redirect_uri=http://myapp.com&scope=openid profile email&state=someLongStateIdentifier
Here are the claims I got in the response from the UserInfo end point request:
{
"sub": "...",
"name": "...",
"given_name": "...",
"family_name": "...",
"picture": "...",
"email": "...",
"email_verified": true,
"locale": "..."
}
Notice how they are a subset of all of the supported claims... Can anyone tell me why I am not getting all of the supported claims in my response?
You won't always get back all of the supported claims. Supported claims means exactly that - they're supported. What determines if they're returned from the userinfo endpoint or not is if your user has values for those associated claims. Also, Google could also be filtering out claims that aren't directly related to the user... because after all it's the userinfo endpoint.

Resources