Decoding jwt token returned by the new google identity api - google-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

Related

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

AWS Cognito with ADFS: Issuer doesn't match providerName

I'm attempting to set AWS.config.credentials in a server-less lambda (running in a REST endpoint). The login is from a Cognito User Pool which uses ADFS as a Federated Identity. I also have a Cognito Identlty Pool in the mix.
My lambda handler has event, context, and callback parameters like this:
exports.handler = (event, context, callback) {
...
}
The event.headers.Authorization has a valid JWT (from the id_token) which was placed in the Authorization header when the POST call on the HTML client to the REST endpoint was executed. I validated the JWT myself here: https://jwt.io. In addition, the context.authorizer object also looks to be valid indicating that I am logged in, it has my email and username information and valid issuance and expiry dates. In short, I'm pretty sure I've got a valid login.
But when I try to execute the following commands I get an error that says "NotAuthorizedException: Invalid login token. Issuer doesn't match providerName"
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:my-identity-pool-id',
Logins: {
"cognito-idp:us-east-1.amazonaws.com/us-east-1_myUserPoolId": event.headers.Authorization
}
});
AWS.config.credentials.get(err => {
if (!err) {
var id = AWS.config.credentials.identityId;
console.log('Cognito Identity ID '+ id);
} else {
console.log('cred error: ', err);
}
});
of course, my-identity-pool-id and myUserPoolId are replaced with their actual values. I've seen many other posts on this topic indicating incorrectly formulated Logins objects produce this error, but I've been over that several times in this code - and had others look it over for me as well. I don't think I got that part wrong. In so far as the error text is concerned, well it is actually correct. If I look at my decoded JWT, the issuer and the providerName are different:
{
"at_hash": "pciSj0Hcjk1Pp9noJIj4GQ",
"sub": "e41500b1-d987-49be-81c0-8fbc36a59ce1",
"email_verified": false,
"iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxxxx",
"cognito:username": "ge2cadmin2-saml-idp_ravi",
"cognito:roles": [
"MyFancyRole"
],
"aud": "7r7v0ren5fm3m6vmjgj6nasr79",
"identities": [
{
"userId": "ravi",
"providerName": "ge2cadmin2-saml-idp",
"providerType": "SAML",
"issuer": "http://adfs.careevolution.com/adfs/services/trust",
"primary": "true",
"dateCreated": "1590770196859"
}
],
"token_use": "id",
"auth_time": 1591118267,
"exp": 1591126227,
"iat": 1591122627,
"email": "ravi#careevolution.com"
}
As you can see, in the identities[] section, the issuer is http://adfs.careevolution.com/adfs/services/trust and the providerName is ge2cadmin2-saml-idp. But I think in this scenario, that is to be expected. However, if the problem is indeed this difference (and not some misconfiguration elsewhere) how can I setup things so AWS.CognitoIdentityCredentials is OK with the issuer being different from the providerName?
The answer turns out to be simple. The magic string: "cognito-idp:us-east-1.amazonaws.com/us-east-1_myUserPoolId" was incorrect. Instead I should have used: "cognito-idp.us-east-1.amazonaws.com/us-east-1_myUserPoolId" (with a dot after cognito-idp instead of a colon). While the error message is terrible and misleading, the error turned out to be simple user error.
I posted this here in order to help anyone who comes by with a search for that same misleading error message.

Is it possible to use JWT Bearer received from MSAL (MS identity platform) to access MS AppCenter API?

I am writting Android app to call MS Appcenter API (https://openapi.appcenter.ms/) and want the users of the app to login with their credential so i can not use pregenerated tokens.
I am able to get JWT token using MSAL android library com.microsoft.identity.client:msal:0.2.2
If I try to call the API "https://api.appcenter.ms/v0.1/user" with Authorization: Bearer ... header I got response {"message":"Fail to verify JWT Token.","statusCode":401,"code":"Unauthorized"}
Neither X-API-Token works with the token (not a surprise)
It is interesting if I lookup JWT token from GoogleChrome dev tools and use it either with curl: curl -X GET "https://api.appcenter.ms/v0.1/user" -H "accept: application/json" -H "Authorization: Bearer ..." or hardcode into my android app I get success response.
So it is definitely possible to use "some" JWT token to access Appcenter API, but it is unclear is it possible to get working JWT token from MSAL and what I should use instead it if it is not possible.
My first suggestion is I have to use the correct "SCOPE" audience, but it is not documented and quick guesses did not help
The way I get JWT token from MSAL (have success to get it)
val SCOPES = arrayOf("email")
val authClientApplication = PublicClientApplication(context, clientId)
authClientApplication.acquireToken(
context,
SCOPES,
object:AuthenticationCallback {
override fun onSuccess(authenticationResult: AuthenticationResult?) {
Log.d(TAG, "Successfully authenticated");
Log.d(TAG, "ID Token: " + authenticationResult?.idToken);
token = authenticationResult?.idToken
}
...
The way I try to use the JWT token (fails with 401 if JWT got from MSAL)
val client = OkHttpClient()
val request = Request
.Builder()
.header("Authorization", "Bearer "+token)
.url("https://api.appcenter.ms/v0.1/user")
.build()
try {
client.newCall(request).enqueue(object: Callback{
...
JWT from MSAL decoded content (not-working):
{
"ver": "2.0",
"iss": "https://login.microsoftonline.com/XXXXXX-6c67-4c5b-b112-36a304b66dad/v2.0",
"sub": "AAAAANNNNNAAAAAAAAAAAAJgzlyDKyV8iUg4I-js52sY",
"aud": "long-numbers-of-azure-app-client-id",
"exp": 1561271401,
"iat": 1561184701,
"nbf": 1561184701,
"name": "My Name",
"preferred_username": "my#email",
"oid": "00000000-0000-0000-cd89-898098098",
"email": "my#email",
"tid": "long-numbers-of-something",
"aio": "BASE64lookingGarbage"
}
JWT from GoogleChrome session decoded content (working):
{
"id": "35980548-0000-4c1d-9d9c-2318dc17a4a1",
"origin": "appcenter",
"iat": 1561184258,
"exp": 1561187858,
"aud": "Bifrost",
"iss": "Heimdall",
"sub": "user"
}

IdentityServer, how to validate token using multiple authorities

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.

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