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.
Related
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.
I have a link that contains a parameter token which is the JWT token that will be validated across another domain. I'm struggling with the fact that a user can copy and share that link across several browsers or with other users.
The jwt payload looks like the following:
{
"userId": "46",
"role": "User",
"nbf": 1589877059,
"exp": 1589963459,
"iat": 1589877059
}
My goal is to secure that link from being shared other than the current user while authenticating that user on the second domain.
Am i missing something or is there any other way to secure a link across different domains? Any suggestion is really appreciated.
Update
An approach I'm trying is storing the last active SessionId in the database and validating it on both sides. In case the token is valid and actual SessionId is different from the last active id, then redirect to some unauthorized page. but the drawback is that the user will require session-authentication instead of cookie authentication since the session will reset each time the user closes the browser but the cookie will remain the same.
I'm using paypal-rest-sdk. Problem I'm facing is, when I'm making an authorizationUrl call, I want to pass some parameters which can be accessed in the redirected URL.
Below is my code
import paypal from 'paypal-rest-sdk';
const openIdConnect = paypal.openIdConnect;
paypal.configure({
mode: "sandbox"
client_id: //MyClientId,
client_secret: //MySecretId,
openid_redirect_uri: `http://myRedirectionEndpoint/account/domestic/paypal/callback?state={accountId:5e8c2291d69ed1407ec86221}`
});
openIdConnect.authorizeUrl({scope: 'openid profile'});
Adding query parameter state gives the error as invalid redirectUri
What is the best way to pass the data that needs to be used after redirection
I think you are slightly misunderstanding how oauth authorization works. Basically if you want to get any data you need to do this AFTER you consume the callback and validate the user in your system as well.
Have you ever seen for Google/github etc openid auth provider returning some data that corresponds to the caller system's data? It's not possible.
You are probably confusing this with webhook where the caller system calls a webhook with some data internally and you capture it. Which is commonly used in payment transactions.
But the auth is slightly different. For auth there are 3 systems.
the actual auth provider (Paypal/google/github) etc.
an Identity provider which basically gets profile data etc and other than for enterprise systems these two systems are simply same.
the caller system which is your NodeJS service in this case.
=> Now caller-system calls the auth provider to get some kind of code generally an auth code. This means the user exists in auth system let's say Google.
=> Then the caller-system calls the identity provider with that auth code checking if the user is there in identity provider(idp) as well and the idp returns access_token, id_token, refresh_token etc (as I said most of the time these are same systems). But consider amazon, let's say you want to login to Amazon with your Google account. You have a Google account alright but you don't have amazon account. So you will get the auth code but will not get the id_token.
=> Now the id_token most of the time contains some basic info of the user in JWT format. But Now the ACCESS_TOKEN is used to do all the other calls to your system(caller system). Now as I said id_token some kind of user data. You can have a db table mapping userid with account number in your NodeJs service.
=> Make an endpoint to get the account number or something which takes access_token and id_token. First validate the access_token and verify the signature of the id_token then decrypt the token to get basic user info. and use that id to fetch the data from your table and use that data.
After Edit:
You can see in the doc:
paypal.configure({
'openid_client_id': 'CLIENT_ID',
'openid_client_secret': 'CLIENT_SECRET',
'openid_redirect_uri': 'http://example.com' });
// Authorize url
paypal.openIdConnect.authorizeUrl({'scope': 'openid profile'});
// Get tokeninfo with Authorize code
paypal.openIdConnect.tokeninfo.create("Replace with authorize code", function(error, tokeninfo){
console.log(tokeninfo);
});
// Get userinfo with Access code
paypal.openIdConnect.userinfo.get("Replace with access_code", function(error, userinfo){
console.log(userinfo);
});
When you get the auth code, you use it to call the paypal.openIdConnect.tokeninfo.create and get the tokens. Then use those tokens to call the paypal.openIdConnect.userinfo.get to get the user Info. Now when you get the userinfo you will be able to create the db row that you wanted to create.
You can add those two below calls in your /callback route.
I am making OAuth 2.0 auth code authentication flow with multi-tenant application.
Here is my authorize url:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=my_id&prompt=consent&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauthorize&response_type=code&scope=openid+offline_access&state=17
It goes fine and I receive auth_code. Then I make request with this auth_code to token_url and receive a lot of information, like:
token_type
scope
id_token
access_token
refresh_token
expires_at
ext_expires_in
Seems fine to me, but when I make request on API with access_token like:
https://management.azure.com/subscriptions/my_sub_id/locations?api-version=2016-06-01
with headers:
Content-Type:
- application/json
Authorization:
- Bearer EwBQA8l6BAAURSN/FHlDW5xN74t6GzbtsBBeBUYAAV1IHgHb4dOWblzfd/YsSuFicAMDYbua17QivnAT9/pIaeKAg3uKsK5VGqWLzjMOUQrCpd7R1RAM6RkzI0u8e4rpO7DISG7qLso5H5+U1jb+38/j1urcwlXMMxhy83ZXmdpkLXpZV+vcOV...
It responds with 401 error
body:
encoding: UTF-8
string: '{"error":{"code":"InvalidAuthenticationToken","message":"The access token is invalid."}}'
To be honest I think something wrong with my access_token. It seems not like JWT for me. Documentation says it looks like:
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCEV1Q..."
But my access_token looks like:
"access_token": "EwBYA8l6BAAURSN/FHlDW5xN74t6GzbtsBBeBUYAAZDe7JE/MPLoAi+Fr+1Xxq5eBe5N9l8Q+c4QjkY5PGEzRnBpPe7+v6h+PLdh1cceBQx+/JsB2QCrYSCt7x/zGsQAhwoY/"
Is it fine?
Here is my permissions for application:
Permissions
The main issue you have here is that you have only asked for an access token for the scopes openid offline_access. The resulting access token will be for Microsoft Graph (https://graph.microsoft.com), not for the Azure REST API (https://management.azure.com).
To indicate you would like a token for a given API, the scope parameter in your authorization request should include the delegated permission you would like the app to have for the API. In the case of Azure REST API, there's only one delegated permission: user_impersonation. The identifier URI for the Azure REST API is https://management.azure.com, so the scope value you want to use is:
openid offline_access https://management.azure.com/user_impersonation
Two more important notes:
As you've discovered, you will not always be issued an access token as a JWT which you can decode peek at. The format of the access token is an agreement between the service which issued the token (Azure AD or Microsoft Accounts, in this case), and the service for which the token was issued (Microsoft Graph, in this example).
You should not always include prompt=consent. prompt=consent should only be used if you have already tried signing in the user without the user needs to be re-prompted for consent for a new permission.
If you simply include the required scopes in the scopes parameter, the Microsoft Identity platform will take care of figuring out if it needs to prompt for consent or not. If you always include prompt=consent, you will find that many organizations will be blocked from accessing your app, because they've disabled the ability for users to grant consent themselves (and this parameter specifically states that you require the user to be prompted again).
I have registered a multitenant app at https://apps.dev.microsoft.com since the "admin consent" prompt wasn't available in the Azure AD apps. Admin consent is required for our app to retrieve info about users and their calendars.
I can provide admin consent from a completely different tenant than what this app is registered from and use the provided access token to retrieve all necessary information, however that obviously expires after an hour and we need offline access.
I have tried using the tenantId instead of 'common' in the https://login.windows.net/common/oauth2/token endpoint, however receive the same message as below.
The following is the data being submitted to the token endpoint in json format (converted within node to form encoded format before submitting):
{
grant_type: 'refresh_token',
client_id: 'e5c0d59d-b2c8-4916-99ac-3c06d942b3e3',
client_secret: '(redacted)',
refresh_token: '(redacted)',
scope: 'openid offline_access calendars.read user.read.all'
}
When I try to refresh the access token I receive an error:
{
"error":"invalid_grant",
"error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID 'e5c0d59d-b2c8-4916-99ac-3c06d942b3e3'. Send an interactive authorization request for this user and resource.\r\nTrace ID: 2bffaa08-8c56-4872-8f9c-985417402e00\r\nCorrelation ID: c7653601-bf96-46c3-b1ff-4857fb25b7dc\r\nTimestamp: 2017-03-22 02:17:13Z",
"error_codes":[65001],
"timestamp":"2017-03-22 02:17:13Z",
"trace_id":"2bffaa08-8c56-4872-8f9c-985417402e00",
"correlation_id":"c7653601-bf96-46c3-b1ff-4857fb25b7dc"
}
This error occurs even when standard consent is used. I have also tried using the node-adal library instead of raw http requests which produces the exact same result.
I note that "offline_access" isn't a permission I am able to set within the MS apps portal, however I would guess the fact that I am getting a refresh token back means that I can refresh the access token?
For the record, the following is the node-adal code I used to see if I was doing something wrong:
var self = this;
var authenticationContext = new AuthenticationContext('https://login.windows.net/common');
authenticationContext.acquireTokenWithRefreshToken(
self.refreshToken,
self.clientId,
self.clientSecret,
'https://graph.microsoft.com/',
function(a) {
console.log(a);
}
);
Any help in getting this refresh process working is appreciated!
Please ensure that the tenant that you using for refreshing token is same as the tenant that you requesting for the access_token.
The refresh token request works well for me unless in the scenario of below:
register the app from protal using Microsoft account
user1 is in tenant1
add user1 as the external users to tenant2
request the access_token/refresh_token from tenant1(OK)
try to refresh the token using tenant1 in the request(OK)
try to refresh the token using tenant2 in the request(same error message)