Node.js authentication server for Firebase Admin SDK - JWT validation issue - node.js

I am working on a project where we are going to be using different services in a microservice architecture, and we would like to also use some Firebase services. I am working on an auth server that is going to mint custom JWT's for use in both Firebase, as well as the other API projects.
We would like to use the Firebase Auth SDK to easily integrate with FB, Google, Twitter etc, but we need to enrich the user's token with more data. Therefore, my thought process is that I'd create a Node.JS auth server that uses the Firebase Admin SDK to do this. The flow would be as follows:
User logs in with favourite provider on client
If login is succesful, the user receives a JWT from Firebase. This is sent to the auth server for validation
If the auth server can validate the token using the admin SDK, create a new custom token enriched with more data, and return this new custom token to the client
Have client re-authenticate with the new custom token, and use it for communication with both Firebase as well as our other API projects (which will mainly be in .NET Core)
Step 1-3 works fine. The problem arises when trying to verify the custom token on the other services.
TL;DR : There are two questions inhere:
When validating custom tokens issued using the Firebase Node.JS Admin SDK, what should I use as the public key? A key extracted from Google's exposed JWK's, or a key extracted from the private key that is used to sign?
In case of the JWK approach, how should I construct the custom token with a kid header?
First, I am in doubt of the proper way to verify it. (Please excuse me, I'm not that experienced creating OAuth flows.) The algorithm used is RS256, so I should be able to verify the token using a public key. As I see it, there are two ways to get this key:
Extract the public key from the private key and verify using this. I can do this and verify successfully on a test endpoint on my auth server, however I feel this is the incorrect way to do it
The other, and more correct way I think, is to use the values from the token to find the JWK's on Google's "/.well-known/openid-configuration/" endpoint for my project, , i.e.
https: //securetoken.google.com/[PROJECT ID]/.well-known/openid-configuration
to retrieve the exponent and modulus for the correct kid (key ID) and create the public key from those.
The token generated from the admin SDK by doing
admin.auth().createCustomToken(uid, additionalClaims).then(function(customToken)
with some custom claims looks something like this:
headers:
{
"alg": "RS256",
"typ": "JWT"
}
payload:
{
"claims": {
"premiumAccount": true,
"someRandomInnerObject": {
"something": "somethingRandom"
}
},
"uid": "<uid for the user>",
"iat": 1488454663,
"exp": 1488458263,
"aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iss": "firebase-adminsdk-le7ge#<PROJECT ID>.iam.gserviceaccount.com",
"sub": "firebase-adminsdk-le7ge#<PROJECT ID>.iam.gserviceaccount.com"
}
I can't seem to get method 2 to work, though. One problem is that the generated token does not have a kid header, and so does not conform to the OpenID spec (AFAIK), which leads to one of two options:
Go with the first approach above. This leads to problems though - if I for some reason need to revoke or reset the private key on the auth server, I need to do it and deploy the changes on all the other services too, making the solution less dynamic and more error-prone.
Generate a similar token manually using one of the libs mentioned at jwt.io, and add the kid from the original Firebase ID token to it's headers.
Problems with number 2:
What should I put as iss, aud and sub, then? The same values as the admin SDK does? If so, isn't that 'cheating', as they are no longer the issuer?
I've tried it (generating a similar copy of the token, but adding the kid of the original token), and I can't seem to verify the generated token using the created PEM key for the kid.
The way I do the latter is this (following a blog guide on the subject):
Go to https://www.googleapis.com/service_accounts/v1/jwk/securetoken#system.gserviceaccount.com and retrieve the modulus (n) and exponent (e) for the relevant kid
Generate the public key using a lib (rsa-pem-from-mod-exp)
Use the key to verify using the 'official' jwt lib
The above results in a public key as such:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAxXpo7ChLMnv1QTovmm9DkAnYgINO1WFBWGAVRt93ajftPpVNcxMT
MAQI4Jf06OxFCQib94GyHxKDNOYiweVrHVYH9j/STF+xbQwiPF/8L7+haC2WXMl2
tkTgmslVewWuYwpfm4CoQFV29OVGWCqwEcbCaycWVddm1ykdryXzNTqfzCyrSZdZ
k0yoE0Q1GDcuUl/6tjH1gAfzN6c8wPvI2YDhc5gIHm04BcLVVMBXnC0hxgjbJbN4
zg2QafiUpICZzonOUbK6+rrIFGfHpcv8mWG1Awsu5qs33aFu1Qx/4LdMAuEsvX9f
EmFZCUS8+trilqJbcsd/AQ9eOZLAB0BdKwIDAQAB
-----END RSA PUBLIC KEY-----
Two things seem to be wrong. One is that the key is different from the one I can extract from the private key. The other is that the one I extract from the private key has these comments instead:
-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
with no 'RSA'. Does this matter? In any case, it doesn't verify.
Finally, did I misunderstand the OpenID flow completely? Are the JWKs generated from a private key that I need as well to verify my JWTs? Should I expose my own JWKs on my auth server for the other services to contact and use instead of Google's? I'm a bit confused as to what the Firebase Admin SDK does and doesn't do, I think :-)
I know this is a lot of questions, but I think they're all related.
Some resources I've relied on in my research (besides the official admin sdk docs ofcourse):
jwt.io
Is it still possible to do server side verification of tokens in Firebase 3?
https://ncona.com/2015/02/consuming-a-google-id-token-from-a-server/
https://stackoverflow.com/a/42410233/1409779
https://andrewlock.net/a-look-behind-the-jwt-bearer-authentication-middleware-in-asp-net-core/

After re-authenticating the Firebase client SDK with the custom token, the client actually generates a new ID token with the claims from the custom token. This ID token is what you should use to verify requests made to your different microservices (documented here). So yes, your original ID token is discarded, but a new one is created in its place. And that ID token will be automatically refreshed every hour. So, you should be able to just call user.getToken() to get a valid ID token whenever you need it. That method handles all the caching on your behalf.

Related

How to decode custom token generated using firebase admin NodeJS sdk

I am using firebase nodejs admin sdk to generate a custom token that is later sent to android client. I have been successful with generating the token using admin.auth().createCustomToken(). However, I need to decode this token to get the uid and other custom claims that was set when generating the token.
I have searched and it seems firebase do not have an out of the box solution using it's admin sdk to decode the custom token (Correct me if i'm wrong).
The solution I have found is using signInWithCustomToken() to first get the idToken and then using firebase's admin sdk verifyIdToken method to get the uid.
I'm a bit skeptical about this solution as I don't think it can provide access to the custom claims I set originally.
The other solution I've found is using jwt nodejs module to decode this token. However, jwt requires an API key to decode this token. Not sure about the API key since I used a service account to generate the token in the first place.
Now the questions:
How best can I decode this token in nodejs?
Do the admin sdk for firebase not have any built in method to decode this token directly just as it was able to create the token?
Edit
As Doug has pointed, the reason I need to decode the token is to get the uid (string) and an additional claim which has the user_id (int) associated with a postgres users table that was set during signup. These ids are used to authorise certain http requests
Also I happen to append the custom token as a query parameter to a password reset link. Hence I need to decode the token to know which user owns it.
Found a fix. I used id tokens. After I signed in using signInWithCustomToken with firebase android sdk, the sdk actually generates an id token. The id token rather than the custom token should be sent to the server on http request. By using the admin.auth().verifyIdToken(idToken) method in the firebase admin nodejs environment (on the server), we are able to decode the token to get both the uid and custom claims originally set.
The decoded token looks like this:
{
"is_id_verified": false,
"id": 4,
"iss": "https://securetoken.google.com/my-app-name",
"aud": "my-app-name",
"auth_time": 1628738368,
"user_id": "email#gmail.com",
"sub": "email#gmail.com",
"iat": 1628738369,
"exp": 1628741969,
"firebase": {
"identities": {},
"sign_in_provider": "custom"
},
"uid": "email#gmail.com"
}
The token you use with signInWithCustomToken is just used to sign user in on the client side. The ID Tokens remain the same irrespective of the method of authentication and can be retrieved by mUser.getIdToken() method. Thereafter you can use the Firebase Admin SDK to verifyIdToken or use any third party JWT library whichever is easier.

JWT Issue with Docusign API

We generated JWT using docusign given private key and validated by Docusign public key in jwt.io site. It generated valid signature.
Using same signature we called Docusign demo server for access token
POST https://account-d.docusign.com/oauth/token
with
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
assertion=Signature generated
but getting error "Invalid Grant".
What could be the possible reason? If signature is already verified in jwt.io with public key, Docusign should accept the assertion value.
As documented, you also need to supply the following claims:
iss--The integration key (also known as client ID) of the application.
sub--The user ID of the user to be impersonated.
iat--The DateTime when the JWT was issued, in Unix epoch format.
exp--The DateTime when the JWT assertion will expire, in Unix epoch format. Use 1 hour after iat or less.
aud--domain name of the authentication service instance to be used. For demo environments, use account-d.docusign.com For production environments, use account.docusign.com. Note: Do not include https:// in the aud value!
scope--The scopes being requested. For the JWT bearer grant, the requested scope should be signature.
See the docs and also see the DocuSign JWT code examples, the repos named eg-01-*
Ask a new question if you'd like further help.

Using JSON Web Tokens (JWT) with Azure Functions (WITHOUT using Active Directory)

I am sure someone out there has already done this, but I have yet to find any documentation with regard to the Microsoft implementation of JWT. The official documentation from Microsoft for their JWT library is basically an empty page, see:
https://learn.microsoft.com/en-us/dotnet/framework/security/json-web-token-handler-api-reference
So, here is what I (and I am sure many others) would like to accomplish:
Definition: User ID = The username or email address used to log into a system.
AUTHENTICATION:
A user logs in. The user fills in web form and the system sends (via HTTPS POST) the users ID and password (hashed) to the server in order to authenticate / validate the user.
Server Authenticates user. The users ID and password are checked against the values saved in the database and if NOT valid, an invalid login response is returned to the caller.
Create a JWT Token - ???? No documentation available!
Return the JWT token to the caller - ???? - I assume in a header? via JSON, not sure -- again - no documentation.
Given the code below, can anyone provide a code example for steps 3 and 4?
[FunctionName( "authenticate" )]
public static async Task<HttpResponseMessage> Run( [HttpTrigger( AuthorizationLevel.Anonymous, "get", "post", Route = null )]HttpRequestMessage req, TraceWriter log )
{
// Step 1 - Get user ID and password from POST data
/*
* Step 2 - Verify user ID and password (compare against DB values)
* If user ID or password is not valid, return Invalid User response
*/
// Step 3 - Create JWT token - ????
// Step 4 - Return JWT token - ????
}
AUTHORIZATION:
Assuming the user was authenticated and now has a JWT token (I am assuming the JWT token is saved in the users session; if someone wants to provide more info, please do):
A POST request is made to an Azure Function to do something (like get a users birth date). The JWT token obtained above is loaded (from the POST data or a header - does it matter?) along with any other data required by the function.
The JWT token is validated - ???? No documentation available!
If the JWT token is NOT valid, a BadRequest response is returned by the function.
If the JWT token is valid, the function uses the data passed to it to process and issue a response.
Given the code below, can anyone provide a code example for steps 1 and 2?
[FunctionName( "do_something" )]
public static async Task<HttpResponseMessage> Run( [HttpTrigger( AuthorizationLevel.Anonymous, "get", "post", Route = null )]HttpRequestMessage req, TraceWriter log )
{
// Step 1 - Get JWT token (from POST data or headers?)
// Step 2 - Validate the JWT token - ???
// Step 3 - If JWT token is not valid, return BadRequest response
// Step 4 - Process the request and return data as JSON
}
Any and all information would really help those of us (me) understand how to use JWT with Azure (anonymous) functions in order to build a "secure" REST API.
Thanks in advance.
Any and all information would really help those of us (me) understand how to use JWT with Azure (anonymous) functions in order to build a "secure" REST API.
Per my understanding, you could use the related library in your azure function code to generate / validate the JWT token. Here are some tutorials, you could refer to them:
Create and Consume JWT Tokens in C#.
Jwt.Net, a JWT (JSON Web Token) implementation for .NET
JWT Authentication for Asp.Net Web Api
Moreover, you could leverage App Service Authentication / Authorization to configure the function app level Authentication / Authorization. You could go to your Function App Settings, click "NETWORKING > Authentication / Authorization" under the Platform features tab. Enable App Service Authentication and choose Allow Anonymous requests (no action) as follows:
You could create a HttpTrigger function with anonymous accessing for user logging and return the JWT token if the user exists. For the protected REST APIs, you could follow the code sample below:
if(System.Security.Claims.ClaimsPrincipal.Current.Identity.IsAuthenticated)
{
//TODO: retrieve the username claim
return req.CreateResponse(HttpStatusCode.OK,(System.Security.Claims.ClaimsPrincipal.Current.Identity as ClaimsIdentity).Claims.Select(c => new { key = c.Type, value = c.Value }),"application/json");
}
else
{
return req.CreateResponse(HttpStatusCode.Unauthorized,"Access Denied!");
}
For generating the JWT token used in App Service Authentication, you could follow How to: Use custom authentication for your application and the code under custom API controller CustomAuthController from adrian hall's book about Custom Authentication to create the JWT token.
UPDATE:
For the custom authentication approach under App Service Authentication, I just want op to leverage the authentication / Authorization provided by EasyAuth. I have did some test for this approach and found it could work on my side. Op could send the username and password to the HttpTrigger for authentication, then the HttpTrigger backend need to validate the user info, and use Microsoft.Azure.Mobile.Server.Login package for issuing App Service Authentication token to the client, then the client could retrieve the token from the AuthenticationToken property. The subsequent requests against the protected APIs could look like as follows:
https://<your-funapp-name>.azurewebsites.net/api/<httpTrigger-functionName>
Header: x-zumo-auth:<AuthenticationToken>
NOTE:
For this approach, the related HttpTrigger functions need to allow anonymous accessing and the App Service Authentication also needs to choose Allow Anonymous requests (no action). Otherwise, the App Service Authentication and function level authentication would both validate the request. For the protected APIs, op needs to manually add the System.Security.Claims.ClaimsPrincipal.Current.Identity.IsAuthenticated checking.
Try this: https://liftcodeplay.com/2017/11/25/validating-auth0-jwt-tokens-in-azure-functions-aka-how-to-use-auth0-with-azure-functions/
I successfully made it work using this guide. It took awhile due to nuget versions.
Follow that guide properly and use the following nuget versions
IdentityModel.Protocols (2.1.4)
IdentityModel.Protocols.OpenIdConenct (2.1.4)
IdentityModel.Tokens.Jwt (5.1.4)
Oh and, the guide tells you to write your AUDIENCE as your api link, don't. You'll get unauthorized error. Just write the name of your api, e.g. myapi
If you get error about System.http.formatting not being loaded when running the function, try to reinstall NET.Sdk.Functions and ignore the warning about AspNet.WebApi.Client being restored using .NETFramework. And restart visual studio.
What you're describing is something that you should be able to do yourself by doing a little bit of research. To address your specific questions:
Create a JWT Token - ???? No documentation available!
The link Bruce gave you gives a nice example for how to create a JWT: https://www.codeproject.com/Tips/1208535/Create-And-Consume-JWT-Tokens-in-csharp
Return the JWT token to the caller - ???? - I assume in a header? via JSON, not sure -- again - no documentation.
There's no documentation because you're basically inventing your own protocol. That means how you do it is entirely up to you and your application requirements. If it's a login action, it might make sense to return it as part of the HTTP response payload. Just make sure that you're using HTTPS so that the token stays protected over the wire.
A POST request is made to an Azure Function to do something (like get a users birth date). The JWT token obtained above is loaded (from the POST data or a header - does it matter?) along with any other data required by the function.
How you send the token is, again, entirely up to you. Most platforms use the HTTP Authorization request header, but you don't have to if you don't want to.
The JWT token is validated - ???? No documentation available!
Use the ValidateToken method of the JwtSecurityTokenHandler (see the previous link for how to get the JwtSecurityTokenHandler). Docs here: https://msdn.microsoft.com/en-us/library/dn451155(v=vs.114).aspx.
I created an Azure Functions input binding for JWT Token Validation. You can use this as an extra parameter with the [JwtBinding] attribute. See https://hexmaster.nl/posts/az-func-jwt-validator-binding/ for source and NuGet package information.
Basically Azure Functions built on top of ASP.NET Core. By making some dependency injection tricks you could add your own authentication and policy-based authorization. I created demo solution with JWT authentication just for fun, beware to use it on production.

PARTNER_AUTHENTICATION failed for Docusign.esign.dll

I am writing code to use docusign demo machine through Docusign.esign.dll . I have tried using Oauth process for connecting the docusign.
I have used the code similar to the code motioned in here.
https://github.com/docusign/docusign-csharp-client/blob/master/test/SdkTests/JwtAuthUnitTests.cs
But I have used my demo machine Integetor key and private key. But I am getting the below error. So do I need to change any setup in my demo machine? Or how do I get valid Integotor key.
I hope my PEM key is causing the issue. So let me know how to preparte that pEM KEy.
I just copied by Private key and created the PEM file using notepad application.
Please let me know do I miss any thing?
Error calling Login: {\r\n \"errorCode\": \"PARTNER_AUTHENTICATION_FAILED\",\r\n \"message\": \"The specified Integrator Key was not found or is disabled. An Integrator key was not specified.\"\r\n}"}
BY default, the API points to their live/production servers. After creating an instance of the ApiClient, set it to point at the demo server:
apiClient.RestClient.BaseUrl = new Uri("https://demo.docusign.net/restapi");
Edit: That was for legacy authentication. For OAuth, please check to make sure you're pointing to account-d.docusign.com (notice the -d).
I too found this to be the issue, in the response the bearer token is missing
<br/><br/>string host = "https://demo.docusign.net/restapi/v2";
// Note with or without v2 their supplied credentials work<br/>
string oauthBasePath = "account-d.docusign.com";<br/>
ApiClient apiClient = new ApiClient(host);<br/>
apiClient.ConfigureJwtAuthorizationFlow(integratorKey, userId, oauthBasePath, privateKeyFilename, expiresInHours);
When you use the credentials from the JwtAuthUnitTests - TestConfig all works
Steps followed should be:
Created demo machine
Created IK
Created Secret key
Created RSA pair key
Copy the private key in to notepad and save that file in location
Missing steps are:
Granting Consent either using User Consent or Admin Consent, check
Service Integration for details.
Configure Redirect URI in the Integrator Key, only needed for User
Consent via Authorization Code Grant
You can use Admin Consent only if you can claim email domain in DocuSign else you need to use User Consent. With User Consent, normally using Authorization Code Grant, you need to get consent with scopes of Impersonation Signature. Once you have user's consent, then you can get new AccessToken for that user using JWT.
Also you need to point to correct host for Demo and Prod,
account-d.docusign.com is required for Demo
account.docusign.com is required for Prod
Above host is used to get access token from DocuSign Account Server (/oauth/token), and you will use above host also for getting the baseUri from /oauth/userinfo endpoint. Other than these two call, I don't think you will use above host.
In response for /oauth/userinfo endpoint call, you will get base_uri and account_id like below
"account_id": "fe0b61a3-3b9b-cafe-b7be-4592af32aa9b"
"base_uri": "https://demo.docusign.net"
You will use above base_uri and account_id for any other API calls, like for creating envelope etc
<base_uri>/restapi/v2/accounts/<account_Id>/envelopes

Why google Oauth verifyIdToken (javascript nodejs version) doesn't use client-secret?

I'm testing google singin for a SPA js+nodejs app.
I've added this:
<script src="https://apis.google.com/js/platform.js" async defer></script>
and these:
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com">
<div class="g-signin2" data-onsuccess="onSignIn"></div>
in html5/js client side.
following this guide:
https://developers.google.com/identity/sign-in/web/sign-in
when the users authenticate the library gets the token and pass it to the server as explained here:
https://developers.google.com/identity/sign-in/web/backend-auth
on server side (nodejs) the token is verified using this function:
client.verifyIdToken(
token,
CLIENT_ID,
// Or, if multiple clients access the backend:
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3],
function(e, login) {
var payload = login.getPayload();
var userid = payload['sub'];
// If request specified a G Suite domain:
//var domain = payload['hd'];
});
MY QUESTION IS: when is the client_secret used? as I've used CLIENT_ID front end to get the auth token from google then I've used CLIENT_ID server side for token verification. I thought that the token could have been verified using client_secret (that is SECRET) known only server side so that no one else getting the token can auth that user.
What am I missing?
It appears the Client you have created is a Public client , The Client Secret is used in a Private Client .
Edit : I am sorry I used the term private client instead of Confidential client .
Basically we have 2 types of clients in Oauth2
Public Clients :- These are clients which don't need a client secret .
Private Clients :- These clients have a Client secret .
I cannot give you a very certain answer as to why you do not get to see your client-secret as I have not worked with these specific libraries before , however
it seems to me that may be you had a created a public client instead of a Confidential one .
I believe I have the answer,
See here: https://firebase.google.com/docs/auth/admin/verify-id-tokens
It explains how to check the signature of the JWT on your own (if you wanted) and this is also what the google-auth-library is doing. Inside the library, search for verifySignedJwtWithCertsAsync() inside of oauth2client.js. Google handles the signing of the JWT using their own private key during the federated sign in process. By the time the JWT is returned to you and sent to the auth library, it's already been signed. This is great because you never have to touch a private key. It's securely stored at Google, you just let Google handle that part. Then, when you send the JWT up to your server, the key id claim in the header lets the auth library know which public key to use to decode it. If the public key fails to decode it, then the authentication has failed.
Finally, ensure that the ID token was signed by the private key corresponding to the token's kid claim. Grab the public key from https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com and use a JWT library to verify the signature. Use the value of max-age in the Cache-Control header of the response from that endpoint to know when to refresh the public keys.
If all the above verifications are successful, you can use the subject (sub) of the ID token as the uid of the corresponding user or device.

Resources