When used Sub and Aud Claim of payload on a system with JSON Web Token - authentication

I'm conducting tests to understand and implement a system of user authorization through the use of JSON Web Token.
Looking for information about the configuration of a token arises me a couple of questions about the use of two Claim Payload, the Sub and Aud.
{
"iss": "www.miweb.com", // issuer
"iat": 1455550200, // time was issued
"exp": 1455559810, // expiration timestamp
"nbf": 1455550260, // not before
"jti": "31d6cfe0d16ae931b73c59d7e0c089c0", // unique identifier
"sub": "", // ¿subject?
"aud": "", // ¿?
"data": {/* attached data */}
}
From what I've observed is rarely used these two claim. My question then is:
What scenario can give use and for what purpose?
Thank you very much, greetings
Same written in Spanish StackOverflow question: https://es.stackoverflow.com/q/11786/5984
Ps: Sorry for the language, not domain.
Edited: Translation of comments in the code

The Subject ('sub') claim identifies the user or application (in case of client credentials flow) that was authenticated.
The Audience ('aud') claim indicates who the token is issued for.
Suppose my client application needs to call service A of behalf of user X.
Typically, my application would communicate with the authorization server to authenticate the user (for example using one of the OAuth2 grant flows) and request access to service X. The authorization server would authenticate the user and ask for consent.
If the user gives consent, the authorization server will issue a JWT token with a subject claim unique for user X and an audience claim indicating service A.

Related

Clarification on Google Authentication and Authorization, with OAuth in 2022

I am writing an App and am trying to leverage Google for A&A. The app itself relies on access to the users Google Calendar, and so initially I leveraged their updated OAUTH2 library for A&A.
Here is my flow:
User goes to the index.html which has "https://accounts.google.com/gsi/client" script and google.accounts.oauth2.initCodeClient is called with my client_id, scopes, redirect url
<script src="https://accounts.google.com/gsi/client"></script>
<script>
let client;
function initClient() {
client = google.accounts.oauth2.initCodeClient({
client_id: 'xxxxx-xxxx.apps.googleusercontent.com',
scope:
'https://www.googleapis.com/auth/userinfo.profile \
https://www.googleapis.com/auth/userinfo.email \
https://www.googleapis.com/auth/calendar.readonly \
https://www.googleapis.com/auth/calendar.events',
ux_mode: 'redirect',
redirect_uri: 'http://localhost:5000/oauth2callback',
});
}
// Request an access token
function getAuthCode() {
client.requestCode();
}
The user clicks the login button, which kicks off requestCode() and they begin the login flow. They login or select their google account, then besides the unapproved app screen, they get to the consent screen with my requested scopes.
After, they are redirected to my expressjs endpoint and using the "googleapis" library I exchange with id_token for the access and refresh tokens.
...
const { tokens } = await oauth2Client.getToken(req.query.code); //exchange code for tokens
const userInfo = (
await oauth2Client.verifyIdToken({
idToken: tokens.id_token,
audience: config.google.clientID,
})
).payload;
if (!indexBy.email[userInfo.email]) { // check if user exists
const newUser = {
name: userInfo.name,
email: userInfo.email,
o_id: userInfo.sub,
picture: userInfo.picture,
r_token: tokens.refresh_token,
};
...
Ok, all good.... but not quite. The problem is, that next time the user wants to login to the app, they go through the entire flow again, including the consent screen (again).
So, after going through more docs, even looking at examples from google. I was surprised and I noticed that many of those apps used the passport oauth2 plugin :( Something i've done in the past, but was hoping to avoid that with the recently updated Google client and nodejs libraries.
Ok, how to not prompt for consent screen on subsequent logins?
Maybe separate A&A, so first I use "Sign In With Google" for Authentication, then when I get the user info, check if the user is already registered (hence I have already saved the refresh token) and they start the app.
On the other hand, if they are new (not in existing app user collection), after authenticating, I will then call the OAUTH2 authorization redirect, so again they on Googles site, this time to do the scopes api confirmation.
So, first question, is that the best practice with most apps with leverage a Google API via OAuth? To first Authenticate, then possibility Authorize (as needed). Hopefully this will still work ok when things come up with expired/invalid refresh token (fingers crossed the default google library handles that).
When doing the Authorize for consent, can I pass something from the previous Authenticate flow so they don't need to do that again.
Or maybe when doing the Authenticate process (Google Identity Service), there is some flag or param so that if they have already consented, they don't have to do that again on subsequent logins.
Incase I wasn't clear, in a nutshell the question is: should I be doing Authenticate for login, separately from Authorization (oauth2 token). Or should I go right into the Authorization flow, which first Authenticates the user, and can I skip the Authorization consent screens if they've already done that. Or maybe there's another way which is the best practice.
Thanks for your attention.
Background info
Authentication is the act where by a user logs in into a system using their login and password. With authentication we know that the user is behind the machine. For this we use Open id connect, which was built on top of Oauth2. Open id connect returns and id_token which can be used to identify the user, it is often a jwt containing some claims to identify the subject or the user behind the Authentication.
The scope used for open id connect is profile and email. open id connect grants you consent to access a users profile information.
This is an example of the decrypted id token returned by google from a simple call using profile scope only. All this id token is telling you is who the user behind the machine is.
{
"iss": "https://accounts.google.com",
"azp": "4074087181.apps.googleusercontent.com",
"aud": "4074087181.apps.googleusercontent.com",
"sub": "1172004755672775346",
"at_hash": "pYlH4icaIx8PssR32_4qWQ",
"name": "Linda Lawton",
"picture": "https://lh3.googleusercontent.com/a-/AOh14GhroCYJp2P9xeYeYk1npchBPK-zbtTxzNQo0WAHI20=s96-c",
"given_name": "Linda",
"family_name": "Lawton",
"locale": "en",
"iat": 1655219027,
"exp": 1655222627
}
In the same call google also returned an access token. Now my call contained only the scope for profile, due to the fact that its open id connect. This means that I will only have access to the data that the profile scope would grant access to. In this case most of what is behind the Google people api.
Note: The user does not see a consent screen with open id connect, even though they are consenting to profile scope. It is assumed by signing into your account that the system you are logging into would have access to your profile info.
Authorization
Authorization is the process by which a user grants your application authorization to access their private user data. The user is shown a consent screen where they consent to your application accessing data defined by some scopes.
In the case of google calendar api there are serval
https://www.googleapis.com/auth/calendar See, edit, share, and permanently delete all the calendars you can access using Google Calendar
https://www.googleapis.com/auth/calendar.events View and edit events on all your calendars
https://www.googleapis.com/auth/calendar.events.readonly View events on all your calendars
https://www.googleapis.com/auth/calendar.readonly See and download any calendar you can access using your Google Calendar
https://www.googleapis.com/auth/calendar.settings.readonly View your Calendar settings
In this case you are only given an access token this is again Oauth2 it is authorization to access the users calendar data it is not authentication this is not related to login.
Your question
So, first question, is that the best practice with most apps with leverage a Google API via OAuth? To first Authenticate, then possibility Authorize (as needed).
You would do both at the same time.
When you authencation your user make sure to include your google calendar scope then the access token and refresh token returned will grant you access to google calendar.
I am going to assume that you have some kind of user system. When you store the user be sure to store the refresh token that is returned.
As far as Authentication goes i will assume you either have a remember me system which will set a cookie on their machine and remember the user so that you can then get the refresh token from their system the next time they come back.
If they did not chose to select a remember me option then will then have to login every time they visit your site but part of the login will return the "sub": "1172004755672775346", this is the users id on google system so you can use that in your database to match the user when they come back.
Your question is quite complex and will depend upon the type of system you have what it is designed to do as well as what programming language you are using. That being said I hope this very long answer clears things up a bit.

Using JWT - is it fine to authenticate user with the subject being their email?

I'm new to authentication, and just trying out JWT authentication on a small express app.
I've got a user authentication setup using JWTs, and I'm using the subject as the user's email.
Is this a good practice?
If I decode the JWT on jwt.io, I see:
{
"sub": "test_user_3#test.com",
"iat": 1489963760,
"exp": 1490568560
}
Is that how it is supposed to work?
The sub claim must be unique. Since email addresses are unique, it is a reasonable choice for the claim.
See RFC7519
4.1.2. "sub" (Subject) Claim
The "sub" (subject) claim identifies the principal that is the
subject of the JWT. The claims in a JWT are normally statements
about the subject. The subject value MUST either be scoped to be
locally unique in the context of the issuer or be globally unique.
The processing of this claim is generally application specific.
Ensure two users do not register theirselves with the same email address, for example using a generic email like info#test.com

What are the payload requirements for ASP Net Identity JWT API tokens

I have an ASP.NET Web Api that uses ASP.NET Identity to manage user and role access to the API. I have a case where I am trying to create a JWT token so an external company can reach one of my endpoints. I have been trying to create my own JWT using the code shown in this SO article - Second answer. My JWT token is decoding properly but does not allow access to my endpoint even though I have the proper role assigned. I am wondering if perhaps other information is required in the payload because the system is based on ASP.NET Identity.
Here is the payload that I included in my JWT
{
iss: "http://mycompany.com",
name: "Company Test",
role: "CompanyTest",
aud: "<Audience ID of my application>",
exp: 1485433642
}
Here are some payloads that I left out of my JWT creation that are included in the JWT tokens generated by ASP.NET Identity. I am wondering if they are essential and therefore must be included in the payload of the JWT.
{
"nameid": "<user unique identifier here from Identity table>",
"unique_name": "<Email account of user>",
"http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider": "ASP.NET Identity",
"AspNet.Identity.SecurityStamp": "<Security stamp column from Identity table>"
}
I left these values out when trying to create my own JWT token because the I was not tying the access to a specific account. I just wanted to create a long lasting token that the external company could use without having to log in.
I figured out the answer to the question so I figured I would post it here. You do not need the extra payload object. Turns out the issue I was having was the iss: payload. I had set it to a production environment but was testing locally. Once I changed the iss: http://localhost:12345 I was able to access the endpoint even though the user was not in my ASP.NET Identity system.
So just to confirm .. all I needed was the following data in my payload
{
iss: "http://localhost:12345",
name: "Company Test",
role: "CompanyTest",
aud: "<Audience ID of my application>",
exp: 1485433642
}

How to force AD FS 3.0 (Windows 2012 R2) to send nbf (not before) in jwt

I am trying to receive JWT token from oauth2 endpoint of ADFS in my single page application. I succesfully received code from oauth2 endpoint. After ajax POST request i received access_token and refresh_token. But when i look to access_token i have there only these claims:
{
"aud": "https://localhost/",
"iss": "http://fs.development.org/adfs/services/trust",
"iat": 1438015081,
"exp": 1438018681,
"email": "Test.User#development.org",
"role": "Domain Users",
"unique_name": "Test.User",
"primarysid": "S-x-x-xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xxxx",
"upn": "test.user#development.org",
"auth_time": "2015-07-27T16:40:01.636Z",
"authmethod": "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
"ver": "1.0",
"appid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
As you can see i didn't receive nbf claim from AD FS. I cannot find it in configuration of my relying party trust. I tried to set property NotBeforeSkew to two minutes and TokenLifetime to 60 minutes on my relying party in hope that AD FS start sending nbf claim. But i was wrong, nothing helps in any way.
So my question is this. It is possible to force from my application or from ad fs server to send nbf claim?
Maybe it is just matter of configuration but i wasn't able to deduce from documentation how this claim can be configured.
The only way I have found to fix this is add a custom claim rule to ADFS. This feels like a bit of a hack, but I simply set 'nbf' to zero and then any consumer will at least not complain about a lack of this property. Although 'nbf' is in fact optional Sharepoint 2013 seems to deem it mandatory when using OAuth which was the original reason in my case that I needed to supply some value. Here is my custom claim rule for anyone struggling with this:
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"]
=> issue(Type = "nbf", Value = "0");

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