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.
Related
I am integrating my API backend with DocuSign in order to send and retrieve envelopes. I am using the JWT Grant flow.
Authentication options
In the DocuSign development environment, I am able to retrieve an access token using the JWT flow and the Docusign C# SKD.
I need to then call the oauth/userinfo endpoint in order to retrieve the base_uri field to use for making calls to Docusign.
When I make a GET request to https://account-d.docusign.com/oauth/userinfo, including the access token in the Authorization header as Bearer <access_token>, I receive a 401 Unauthorized response, and this message in the body:
{
"error": "internal_server_error",
"reference_id": "<some GUID>"
}
I have tried this using curl, Postman and the C# SDK and all give the same result.
Curl syntax:
curl --header "Authorization: Bearer <access token>" https://account-d.docusign.com/oauth/userinfo
user-info endpoint documentation
JWT flow (step 4)
As far as I can see, I appear to be calling the API according to the documentation and I have set up the account with an RSA key pair which is required for system integrations (created within the Docusign admin portal).
Can anyone think of a reason this could be happening?
Since you're using the C# SDK as you mentioned, you can call this endpoint using the same SDK if you have a valid token.
https://developers.docusign.com/docs/esign-rest-api/sdk-tools/c-sharp/reference/
public OAuth.UserInfo GetUserInfo(string accessToken);
You can confirm that your token is valid by trying to use it for other API calls.
A token from the developer account should be useful to make this call in the developer account only. If you need this for production (typically reason to need the base_uri) then you have to call it with account.docusign.com not account-d.docusign.com.
I have now been able to get the base_uri from UserInfo endpoint using the RequestJWTUserToken method in the C# SDK. Using this token allows me to hit the REST API endpoint.
Both methods appear to hit the same oauth/token endpoint and use the same grant type, only RequestJWTUserToken includes the "sub" claim for the userId.
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.
So I know how to create tokens and how to read tokens but I am running into an issue with getting the User_Metadata from the Users I created in Auth0 (without login them in from my application).
What I am trying to do is this:
User some where with a device logs into Auth0 and generates a JWT
Token
User now calls my API and passes Bearer with token in header
I read Bearer and Authenticate that the token is good. I then want to
pull the user information from the token to use to make sure they
have rights to do something.
I am not wanting a 2nd database that holds user information that they will need to log into my API so I know who they are. I just want to be able to use the JWT Token to get that information. Right now when I create a token I have this in the Payload:
{
"iss": "https://.....",
"sub": "RTMLeICuyL1kyeQN#clients",
"aud": "https://.....",
"exp": 1494031764,
"iat": 1493945364,
"scope": ""
}
If I go to Auth0 User Details tab I can see the user and the user_metadata and app_metadata that I want to return but not sure how to get it. Thanks for any help.
I was not able to get the User Profile data from Auth0 to come in on the JWT Token but I was able to use the client scopes in Auth0 to create the scopes needed to do Authorization. This is still not the best answer but it will allow me to determine if someone has rights to view that object and records.
I will go into more details as I write up an example.
I know this issue was asked a lot here, but I still cannot seem to find the exact answer that can solve my problem.
I wish to access Firebase using REST calls, by adding an access_token param.
The access_token is created using the Node.js Admin SDK, using the following code:
var admin = require("firebase-admin");
var serviceAccount = require("./pk.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://XXX.firebaseio.com"
});
var uid = "1234";
admin.auth().createCustomToken(uid)
.then(function(customToken) {
// Send token back to client
console.log("Token: "+customToken);
})
.catch(function(error) {
console.log("Error creating custom token:", error);
});
The problem is that if I take the token created from Node.js and use it my REST call, I get an Unauthorized request error.
I have read in some questions that people added the scope param when issuing the token, but have not found a way to do that with Node.js Admin SDK.
Google's docs are not so detailed with this issue. Any idea what I might try to resolve this one?
The token you are using to authenticate to the Firebase REST API is not the correct type of token. You are using a Firebase Auth custom token, which can only be used to authenticate one of the Firebase client SDKs via the signInWithCustomToken() method as explained in Sign in using custom tokens on clients.
In order to authenticate to the Firebase REST API, you have two options: Firebase ID tokens (for user-based access) or Google OAuth2 access tokens (for admin access).
Authenticate with Firebase ID Tokens
See Retrieve ID tokens on the client for an explanation of how to retrieve access tokens in the various Firebase client SDKs. You can also exchange a Firebase custom token for an ID token and refresh token pair via an undocumented REST API:
Endpoint: https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=<API_KEY>
Method: POST
Request Body: { "token": <CUSTOM_TOKEN>, "returnSecureToken": true }
<API_KEY> is the same API key you get from your Firebase Console that you use in the Firebase clients. <CUSTOM_TOKEN> is a Firebase custom token.
Since ID tokens expire after an hour, you will need to use the refresh token to refresh them via this other undocumented REST API:
Endpoint: https://securetoken.googleapis.com/v1/token?key=<API_KEY>
Method: POST
Request Body: { "refresh_token": <REFRESH_TOKEN>, "grant_type": "refresh_token" }
<API_KEY> is the same API key as before. <REFRESH_TOKEN> is the refresh token from the previous API call.
Once you have an ID token, you can pass that to the REST API via the auth query parameter to authenticate a request. The request respects Firebase Security Rules as if the end user logged into the client was making the request.
Authenticate with Google Access Tokens
To authenticate with a Google OAuth2 access token, the first thing you need to do is get one. See Retrieving an access token for an explanation of how to do this. It only currently includes a Java example, but this is possible in many languages, including Node.js. Once you have an ID token, you can pass that to the REST API via the access_token query parameter to authenticate a request. The request will be made with admin access, overriding all Firebase Security Rules and granting full read and write access.
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.