How to get the Username with OpenID Connect on ADFS Server - single-sign-on

I just realized that the userinfoendpoint doesn't give any useful info about the logged in user.
Now, I saw that you can get an id_token from ADFS tokenendpoint. This id_token is actually a JWT which contains the unique_name.
Now my question is, if it is safe to use this info without signature validation (since we don't have the private key of the HS256 algorithm and validation is as far as I understood, a thing for the issuer not for the client).

• There are two scenarios to your question of whether it is safe to use this info from an ID_token. First, your client, i.e., the application or authorization server will validate the token just like it validates the access token for any tampering. Also, it can validate the issuer to ensure that the correct issuer has sent back the token. Since there are many libraries to validate an ID token, you should use them for ID token validation purposes.
• Also, only confidential client applications should validate an ID token as their token holds a secret while public applications don’t benefit from validating an ID token as there is always a risk of a malicious user who can intercept and edit the keys used for validation of the token.
• As you are speaking of the case when you don’t have the private key to the algorithm used for encrypting the ID token, it must be a confidential client application. Thus, validating the ID token on the lines of access token and confirming the below claims are validated by your token validation library, you can then use the information retrieved from these claims for your purpose. The claims that need to be validated are as follows: -
Timestamps: the iat, nbf, and exp timestamps should all fall before or after the current time, as appropriate.
Audience: the aud claim should match the app ID for your application.
Nonce: the nonce claim in the payload must match the nonce parameter passed into the /authorize endpoint during the initial request.
Please find the below documentation link for more in-depth details on validating the tokens and the inherent process involved in it: -
https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens#validating-tokens

No, sadly the userinfo endpoint only gives you a "sub".
You can validate the JWT yourself.
You should always check the signature.

Related

Is it a best practice to validate the JWT payload after verifying?

My question assumes the implementation of the JWT is sound, and it's more about what you do afterwards with the valid payload.
I am issuing JWT tokens, and then each token is verified to confirm the identity of the user. But is it necessary to still validate the fields once the token has been validated?
For example, here's a sample token payload:
{
“iss”: “example.com”,
“exp”: 1426420800,
“company”: “example”,
“userid”: 123456789,
"name": "Bob"
}
Do I need to validate that 'userid' is an integer? And validate 'name' is a string of a certain length?
Do I need to validate that userid is an integer? And validate name is a string of a certain length?
Assuming that your JWT implementation is sound as you mentioned in the question and assuming that you trust the server that issued the token, such level of validation is not necessary after checking the signature.
Before accepting the token you must ensure that the token is valid. Here are some validations you must consider:
Validate the signature to ensure that the token was not tampered with.
Ensure that the token is not expired by checking the exp claim.
If you use the nbf claim, ensure that it's not too early to accept the token.
Validate other claims such as iss if you need to.
Most of sound JWT implementations support those validations.
As mentioned in Lutz Horn's answer, you should rely on JWT for authentication (who the caller is) only. For authorization (what the caller can do), look up the caller roles/permissions from your persistent storage to get the most updated information.
This depends on your server. If it will do anything using the JWT claims, for example store some data, it should validate that the executing user exists and is authorized to do this.
The JWT only handles authentication: Prove that the user is who he is and that he presented the necessary information (password) to prove this. JWT does not handle authorization: check what the authenticated user is allowed to do.

OIDC - What's to stop someone from spoofing a JWT access_token?

When you authenticate with an OIDC provider you get back an id token and if you specified scopes for an API you get back an access token so that client applications can make requests to protected resources on the end user's behalf. Typically the access token is also a JWT.
But what is to stop someone from spoofing one of these access tokens, and creating one and passing it to an API? I understand there are safeguards to prevent modification because the signature will be different than what any validation logic is expecting, but what if a malicious user created a brand new one manually? Especially because these tokens can be validated 'in place' by any API that requires an access token (not all API's use the introspection endpoint... especially with a JWT). I do understand there is metadata around the signing keys for JWT's from OpenID Connect providers and that it is available in the OIDC discovery document. For example, here is Google's JWK metadata. Given that you have signing information publicly available, and JWT access token's can be validated without any requests to the OIDC provider, how are JWT's secure? What is preventing a person from creating one and passing it as a bearer token to an API that requires an access token?
But what is to stop someone from spoofing one of these access tokens, and creating one and passing it to an API?
Spoofing and reconstruction of signature is nearly impossible without the private key (assuming you are using asymmetric signing algorithm like RS256) that used for signing the original JWT.
The JWK information available via OIDC discovery document only contains the public key.
Also Use HTTPS for authorization / token exchange to avoid token sniffing.

JSON Web Token (JWT)

I have a general question regarding JSON Web Token (JWT).
If the JWT is stolen from the client (say, it was stored as a cookie or the app's database) by hacking or physical access, it can be used to send to the server which the server will think it is the legitimate user. Is this correct?
Is there any common or standard practice to guard against this, for example, by sending the type of device/browser or some reference code together from the client and the server checks it matches additional data the JWT token was generated and stored with. (However, I read that the standard practice is not to store anything on the server.)
Please advise as I need to implement Java JWT (JJWT), RESTful Java Jersey and Google Web Toolkit. (I've been reading documentation such as this: [https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage]).
Thank you!
Possesion of a JWT is the proof of authentication. An attacker who stoles a token can impersonate the user.
So, keep tokens secure:
use a TLS channel
add extra security measures depending on the type of storage. Cookies are vulnerable to CSRF attacks. use HttpOnly if you do not need to access token from javascript. LocalStorage is vulnerable to XSS attacks
set short expiration time on authentication tokens and require credentials if token is expired
Blacklisting is not useful because you won`t know that a JWT has been stolen. And its usage breaks stateleness, one of the advantages of JWT
Additionally is possible to add the IP the token, but consider the usage scenario because it can be problematic on mobile devices or systems behind a proxy
On the client you are building the JWT like:
byte[] key = getSignatureKey();
String jwt = Jwts.builder().setIssuer("myTestApplication")
.setSubject("myTest")
.setExpiration(expirationDate)
.put("scope", "testing")
.signWith(SignatureAlgorithm.HS256, key)
.compact();
On the server side you can verify the JWT in regards to the the key and the expiration date exp (and more i.e. creation date, issuer iss, audience aud):
String subject = "notMyToken";
try {
Jws jwtClaims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt);
subject = claims.getBody().getSubject();
//OK, we can trust this JWT
} catch (SignatureException e) {
//don't trust the JWT!
}
Stealing the JWT should be avoided by using SSL,...but if the JWT is stolen there would be the risk of replaying exactly this JWT - right. That's where jti comes in.
The jti (JWT ID) claim provides a unique identifier for the JWT. The
identifier value MUST be assigned in a manner that ensures that there
is a negligible probability that the same value will be accidentally
assigned to a different data object; if the application uses multiple
issuers, collisions MUST be prevented among values produced by
different issuers as well. The jti claim can be used to prevent the
JWT from being replayed. The jti value is a case-sensitive string. Use
of this claim is OPTIONAL.
With this identifier you could recognize if this ID was already sent (you have to blacklist it on the server side which somehow undermines the nature of JWT). Because you should use the expiration date you could clean the IDs if the expiration date leads to a SignatureException.
However, if the 'hacker' stole the JWT out of the database, as you wrote in the question, you have further problems beside the stolen JWT probably, because than the attacker could also steal other data etc.
Hope this helped a bit.

Process JWT token using JWKs Endpoint

I receive two JWTs: an OpenID Connect ID token (id_token) and an Access Token (access_token). The situation with OpenID is more or less clear - I can validate it using a JWKS Endpoint: https://smth.com/JWKS.
as in example (https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples):
HttpsJwks httpsJkws = new HttpsJwks("https://smth.com/JWKS");
HttpsJwksVerificationKeyResolver httpsJwksKeyResolver = new HttpsJwksVerificationKeyResolver(httpsJkws);
jwtConsumer = new JwtConsumerBuilder()
.setVerificationKeyResolver(httpsJwksKeyResolver)
.setExpectedAudience(...)
.setExpectedIssuer(...)
.build();
The question is how to proceed with the Access Token. I can extract from it the userId and userDetails, but I guess I need also to validate it?
If I try to validate the Access Token the same as for the ID Token, I am getting this error:
UnresolvableKeyException: Unable to find a suitable verification key for JWS w/ header {"alg" : "RS256", "kid":"1"}
And indeed there is no key for "kid" : "1", Also this value "1" seems kind of strange?
Am I doing something totally wrong?
It sounds like you are implementing the role of OpenID Connect client or Relying Party. The two tokens, ID token and access token, serve different purposes and should be handled differently by the client. The ID token is intended for the client and enables authentication of the end-user at the client. The client must validate the ID token (verify the signature and validate claims like exp and aud, etc.) before allowing the end-user in. The access token, however, is for the client to use to access resources or APIs but is not directly intended for the client to consume or validate. The access token is opaque to the client and the client shouldn't care or know about its details. In fact, access tokens aren't always JWTs. In OpenID Connect, the access token is used to call the user info endpoint (with the HTTP header, Authorization: Bearer [access token]) to get more claims/info about the end-user.
The value of "1" for the kid is totally legal but it is referring to a key that the AS/OP and the user info endpoint know about somehow. It is not a key at the OpenID Connect JWKS endpoint. "1" isn't a key that the client needs to know about because the client isn't supposed to directly verify the access token.

Do we need to validate JSON Web Token at client/consumer?

I am studying a bit about JSON Web Token. I understood that header+claims get signed by a secret key and the encoded result gets concatenated to "header.claims.signature" and finally sent back to client.
I have some basic doubts:
Do we need to validate the token at client/consumer (once it receives from server, for sake of authenticity)? Is it a standard at all or not necessary? Any example for reference on this?
If the client needs to validate the token, I guess it has to know the secret key to decrypt/decode. Is there any other way to ask client validate on its own without sharing server's secret key?
If client knows the secret key, I guess it can create its own token too. If such is the case, do the server need to accept such tokens (or is application/business dependent?)
Do we need to validate the token at client/consumer
On client side you usually don't validate the token. Treat it just as an opaque token. Keep it safe and use it when making requests to the server.
If the client needs to validate the token, I guess it has to know the secret key to decrypt/decode.
As mentioned above, the client doesn't need to validate the token.
In any cases in which the authentication server (the instance that authenticates the user and issues the token) and the resource server (the instance that owns a proteceted resource and requires a token for authorization) are not the same, signing and validation of the token is usually done with asymmetric algorithms like RS256 in which the private key is used to sign the token and only known by the authentication server, and the public key is used to verify the signature.
If client knows the secret key, I guess it can create its own token too. If such is the case, do the server need to accept such tokens (or is application/business dependent?)
That's why a client should not know the secret key.
When symmetric key algorithms (e.g. HS256), in which the same key is used to sign and verify a signature are used, you can't allow the client to know the key, as it could be abused to create a fake token. Then JWT would be pointless. For asymmetric keys, there's no risk if the client knows the public key.