JWT gem - retrieve public signing key from remote authority URI - ruby-on-rails

I have:
An OpenID Connect identity / token server written in AspNet 6
A resource API that I want to protect with access tokens from the identity server, writen in Ruby / Rails
The issued tokens are assymetrically (public/private key) signed JWTs.
If I was writing the resource API in .Net, I would provide a URI for the token authority when setting up authentication, e.g.
app.UseJwtBearerAuthentication(new JwtBearerOptions() {
Authority = "https://uri.to.my.identity.server"
}
The JWT bearer authentication middleware would use this URI to find and retrieve the public key from the identity server, so that it can be used to validate token signatures.
I am looking for a similar approach when using the Ruby JWT gem (https://github.com/jwt/ruby-jwt)
The RSA example on that site just provides the public key explicitly, e.g.
rsa_private = OpenSSL::PKey::RSA.generate 2048
rsa_public = rsa_private.public_key
decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'RS256' }
I could call the identity server discovery endpoint directly, but I was hoping there might be something part of the library (or another library) that would help with this.

Related

Unable to validate access token signature obtained from Azure AD in order to secure Web API

I am trying to secure my web api (.net core 2.2) with Azure Ad using implicit flow.
I registered my application in Azure AD using the Azure Portal > Azure Active Directoy > App Registrations > New Application Registration:
Name = MyWebApi
Application Type = Web app / API
Sign-on URL = http://localhost:55000
Once this app is created, I opened its Manifest file and changed oauth2AllowImplicitFlow from false to true.
Thats all I did for the app registration in azure portal.
Then I called the following URL manually from my chrome browser to get access_token:
https://login.microsoftonline.com/MY-AD-TENANT-GUID/oauth2/v2.0/authorize?client_id=MY-REGISTERED-APP-GUID&response_type=token&redirect_uri=http%3A%2F%2Flocalhost%3A55000&scope=openid&response_mode=fragment
the response from calling the above url is:
http://localhost:55000/#access_token=MY-ACCESS-TOKEN&token_type=Bearer&expires_in=3600&scope=profile+openid+email+00000003-0000-0000-c000-000000000000%2fUser.Read&session_state=b2be972a-cfbc-49f1-bfc0-6c93f6c87d02
when I pass MY-ACCESS-TOKEN as Bearer token in Authorization header to my Web API (.net core 2.2) I get the following exception:
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: N-lC0n-9DALqwhuHYnHQ63GeCXc'.
I then tried manually verifying the signature:
when I paste MY-ACCESS-TOKEN in https://jwt.io/ the header is:
{
"typ": "JWT",
"nonce": "AQABAAAAAACEfexXxjamQb3OeGQ4Gugvm6YdOT-bkA0IPllKMt06-J8If5AQ075TVCav94X_ZYcEYKaPneqdJcqYry-Z4XjX0eMN_fiJX_8wXe9D2b6eRiAA",
"alg": "RS256",
"x5t": "N-lC0n-9DALqwhuHYnHQ63GeCXc",
"kid": "N-lC0n-9DALqwhuHYnHQ63GeCXc"
}
I then went to this URL to obtain the public key for kid: N-lC0n-9DALqwhuHYnHQ63GeCXc
https://login.microsoftonline.com/common/discovery/keys
I then pasted the following as a public key on jwt.io to validated token signature:
-----BEGIN CERTIFICATE-----
OBTAINED-PUBLIC-KEY-FROM-THE-ABOVE-URL-HERE
-----END CERTIFICATE-----
and I again get Invalid Signature.
I have been to this thread: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/609, but I am not sure why does my token header has nonce value or if this is an issue at all in my case or not.
Any ideas what I am doing wrong here?
I tried this on my side, it worked well.
Request url:
https://login.microsoftonline.com/tenant-name/oauth2/v2.0/authorize?client_id=application_id&response_type=token&redirect_uri=https://snv2app.azurewebsites.net&scope=api://f3d966c0-517e-4e13-a5bb-9777a916b1a0/User.read openid&response_mode=fragment
And when I got the access_token, I parsed it in the jwt.io and entered the public key, I got the result:
What is happening here is the token you are receiving is an access_token for the userInfo endpoint. The audience is graph. Tokens for graph have been modified in a special way so that they must be transformed before the signature can be validated. This allows for graph to forward the token downstream (after transforming) and not worry about a forwarding attack to occur.
To validate see if 'aud == graph'.

Feign Oauth how to manually set the bearer token value?

I'm developping a Rest API, MyApi.
In there, I'm using Feign (and swagger codegen) to generate a client for another API, let's call it Ext-API.
The user will have called Ext-API previously and among other things will have retrieved a JWT Token.
He'll then call my API using Basic Auth and in the body it'll give me the JWT token.
I'm to use this JWT token as auth header to connect to Ext-API from my API and do some more stuff on behalf of the user.
However all example of Oauth & Feign example rely on the Oauth also being used to connect to the API using the generated client.
I can't find how I could, on every request, update the Oauth Token.
There are nothing exposed by feign to do this.
Atm I'm using regular Rest template.
You can used #RequestHeader in feign Client, see below
#FeignClient(name = "<name>", configuration = <configclass>)
public interface Client {
public final String AUTH_TOKEN = "Authorization";
#RequestMapping(method = RequestMethod.GET, value = "users", produces = "application/json")
public HttpEntity<List<User>> getUsers(#RequestHeader(AUTH_TOKEN) String oruToken,
#RequestParam("<param>") Integer value);
}
and from you program you can pass token to feign client

Auth0 JWT Access Tokens

I am having difficulty getting Auth0 to return access tokens in JWT format. I need them in JWT format in order that I can verify them using a Java JWT library.
I am using Auth0 lock to login, and use /oauth/token to get the access token - I have tried setting the audience to our API identifier (in multiple places including the lock auth params, and the /oauth/token payload), without success - the access token is returned however not a JWT.
Alternatively, is there a Java library for validating "native" Auth0 access tokens?
var options = {
auth: {
redirectUrl: '<redirect_link>',
responseType: 'code',
params: {
audience: '<API_identifier>',
scope: 'openid offline_access'
}
},
allowedConnections: ['Username-Password-Authentication']
};
var lock = new Auth0Lock('<clientId>', '<auth0_Host>', options);
lock.show();
The returned code is used to POST to https://<host>/oauth/token with data:
client_id=<client_id>&redirect_uri=<redirect_url>&client_secret=<client_secret>&code=<returned_code>&grant_type=authorization_code&audience=<API_identifier>
Which is successful but the access token is not JWT, for example: "access_token":"sG99DAJI789SYgTj"
Using the scope openid returns an id_token in JWT format, but from reading the documentation this token should not be used for API authorisation.
It seems Auth0 is using OpenID connect, that is an extension of OAuth2. After a successful end-user authentication, the server returns an ID Token in JWT format and an access token
ID Token
The ID Token is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when using a Client, and potentially other requested Claims. The ID Token is represented as a JSON Web Token (JWT) [JWT].
You can validate the ID Token at client side using any JWT library, but the validation rules for access tokens are different
3.2.2.9. Access Token Validation
To validate an Access Token issued from the Authorization Endpoint with an ID Token, the Client SHOULD do the following:
Hash the octets of the ASCII representation of the access_token with the hash algorithm specified in JWA for the alg Header Parameter of the ID Token's JOSE Header. For instance, if the alg is RS256, the hash algorithm used is SHA-256.
Take the left-most half of the hash and base64url encode it.
The value of at_hash in the ID Token MUST match the value produced in the previous step.
So, basically to validate it you need to compute the digest of the access_token using the hash algorithm of ID token, and check that it matches with the at_hash claim of ID Token

How to authenticate a Rails API with Google JWTs

On the frontend, using React and the Google Login Button, the user logs in to my application. Google returns the user's JWT to a callback URL on the frontend, encoded. I then deliver this JWT on each request to a Rails API and want the rails API to decode and verify this JWT, and if it is verified successfully, return the data that API endpoint is supposed to give.
I am currently using this code:
def google_public_key
x509 = OpenSSL::X509::Certificate.new ENV["GOOGLE_CERT"]
x509.public_key
end
The env var is described like this: https://gist.github.com/Connorelsea/c6b91a4b4b6889294fd4e2fcacb06564
I am getting this error: OpenSSL::X509::CertificateError (nested asn1 error)
If I do not verify, I can read the content of the JWT. From the JWT website it can be decoded, but not verified, as well.
Had the same problem and solved by setting the public_key as Pkey object not as a string in the constructor, try:
def google_public_key
x509 = OpenSSL::X509::Certificate.new
x509.public_key = OpenSSL::PKey::RSA.new ENV["GOOGLE_CERT"]
end

Request new access token using refresh token in username-password grant in DotNetOpenAuth

I'm trying to implement a OAuth2 Authorization Server using DotNetOpenAuth. The client is JavaScript based thus incapable of holding any secrets. This is exactly the same problem like this question but with another framework.
The client requests (against the token endpoint) access_token and refresh_token with following parameters:
grant_type: password
username: foo
password: bar
This does work. Now I want use the refresh_token and make a request against the token endpoint with the following parameters:
grant_type: refresh_token
refresh_token: ABCDEF
This gives me the following response:
{"error":"invalid_client","error_description":"The client secret was incorrect."}
Which does make (at least some) sense because RFC6749 states that:
Because refresh tokens are typically long-lasting credentials used to
request additional access tokens, the refresh token is bound to the
client to which it was issued. If the client type is confidential or
the client was issued client credentials (or assigned other
authentication requirements), the client MUST authenticate with the
authorization server as described in Section 3.2.1.
If I change my request like so:
grant_type: refresh_token
refresh_token: ABCDEF
client_id: MYCLIENT
client_secret: CLIENT_SECRET
The problem is my client is not supposed to be confidential (because it is client side JavaScript after all).
This is how the client is defined:
New ClientDescription(ApiKey, New Uri(allowedCallback), ClientType.Public)
I searched through the DotNetOpenAuth source code and found no use of the ClientType. To me it looks like it is not used at all.
It is also not possible to the set an empty client secret, because the DotNetOpenAuth source code actively checkes against this (ClientAuthenticationModules.cs):
if (!string.IsNullOrEmpty(clientSecret)) {
if (client.IsValidClientSecret(clientSecret)) {
return ClientAuthenticationResult.ClientAuthenticated;
} else { // invalid client secret
return ClientAuthenticationResult.ClientAuthenticationRejected;
}
} else { // no client secret provided
return ClientAuthenticationResult.ClientIdNotAuthenticated;
}
If I take a look at MessageValidationBindingElement.cs:
if (authenticatedClientRequest != null) {
string clientIdentifier;
var result = this.clientAuthenticationModule.TryAuthenticateClient(this.AuthServerChannel.AuthorizationServer, authenticatedClientRequest, out clientIdentifier);
switch (result) {
case ClientAuthenticationResult.ClientAuthenticated:
break;
case ClientAuthenticationResult.NoAuthenticationRecognized:
case ClientAuthenticationResult.ClientIdNotAuthenticated:
// The only grant type that allows no client credentials is the resource owner credentials grant.
AuthServerUtilities.TokenEndpointVerify(resourceOwnerPasswordCarrier != null, accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.InvalidClient, this.clientAuthenticationModule, AuthServerStrings.ClientSecretMismatch);
break;
default:
AuthServerUtilities.TokenEndpointVerify(false, accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.InvalidClient, this.clientAuthenticationModule, AuthServerStrings.ClientSecretMismatch);
break;
}
Espacially the comment The only grant type that allows no client credentials is the resource owner credentials grant. makes me wonder. Does that mean that in my scenario the JS client should send username/password along? Nope this will raise the following exception:
AccessTokenResourceOwnerPasswordCredentialsRequest parameter 'grant_type' to have value 'password' but had 'refresh_token' instead.
Which is okay to me, because I don't want the client to keep the password.
So here my questions:
Did I unterstand something fundamentally wrong about the password-grant, refresh_token scheme?
As I see it in a JS client the client_id is public knowledge, so it does not serve any security purpose. Am I correct?
Does it makes sense to change DotNetOpenAuth to make use of the ClientType.Public?
Would it make any difference if I just use client_id and client_secret as not secret? E.g. just supply dummy values? What are the security implications?

Resources