Designing Token Based Authorization Server Request/Response - security

Identity in our services are based on a token stored in a database. This is acquired by the client by logging in with a username and password.
Each time a resource is requested, we plan to validate the token and determine if the user is authorized to access that resource.
Our services are deployed separately, and the authorization server can be reached via HTTP.
What's the best practice/common way in authorizing the requests?
Sending token with requested permission and role
I was thinking of passing in the token with the role and requested permission of the user in the token validation request to the authorization server.
{
token: 'xyz',
role: 'ROLE_ADMIN',
permission: 'SAVE_USER'
}
and respond with: 200 for success, 401 for an invalid token, 403 if they are not authorized to use the permission.
Sending only the token in authorization request
Another approach in mind is sending just the token in the token in the token validation request to the authorization server.
{
token: 'xyz'
}
and respond with all the permissions and roles the user have:
{
roles: ['ROLE_ADMIN', 'ROLE_USER'],
permissions: ['SAVE_USER', 'DELETE_USER', 'SHOW_USER']
}
Which of these are more advisable?
Or are there any other/patterns approaches I can consider?

As you're talking about a "service", it means that most likely your service prefers to know what can it do before it requests for it.
For example you may want to highlight those buttons user can click before a request arrives.
So I think the second approach would be fine. And a kind of first approach is gonna be executed while a request is passed to the server, eg. user wants to remove something and it doesn't have sufficient access, so gets a 403.
Talking about security, in both ways you described, there are sufficient checks, eg. in the first one, when user asks for some action, it GETS CHECKED before getting executed and that's what should be done.

Related

passing properties in callback function nodejs

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.

Wrong access_token from AAD with OAuth2 flow

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).

Unattended authorization into API - no longer possible?

A brief investigation shows that there once existed an authorization mode known as SOBO (for example, see docusign send on behalf functionality), which I find useful in a scenario when an application associates signing requests not with a logged-on user but with some other user. However I am unable to find any mention of it in current documentation; on the contrary, documentation clearly says only 3 grant types are supported, and all three involve impersonated user's actively expressing his consent. No way to just send user credentials, or, alternatively, have user express his consent just once on the docusign admin page, then reuse that consent for, say, one year or forever. Or some other way to avoid end user interaction.
Also, a requirement of using redirect URI to send back continuation data implies one has to either use on-premise solution or make one's application visible on the internet. No way to use public docusign service with on-premise application, right?
DocuSign is discouraging Legacy Header authentication because it requires your integration to hold on to the user's plain-text password. It also cannot support accounts that leverage Single-Sign On or Two-Factor Authentication. The long-term plan is likely to eventually retire it entirely, but there's no timeline for that. Documentation has been retired, but integrations that have used it in the past can still do so.
JWT auth is the equivalent replacement. With individual consent, a user grants the integration access once. Unless the user revokes that consent, the integration will be able to generate access tokens to act as the user indefinitely - JWT consent does not expire.
If you have an Organization with a Claimed Domain, an org admin can grant blanket consent to allow an integration to act as any user under that domain. If you'd like to grant consent to an integrator key owned by your organization, it's as simple as navigating to Org Admin > Applications > Authorize Application. Granting consent to a 3rd party app is similar to the Individual Consent workflow, but has extra scopes as documented here: https://developers.docusign.com/esign-rest-api/guides/authentication/obtaining-consent
Note that while JWT auth does require a redirect URI to be registered, an integration doesn't necessarily need to 'catch' the user after they've granted consent. While it would be recommended that the landing page trigger the user to move forward in the workflow, it's acceptable to point your redirect URI to https://www.example.com, grant consent, and then generate an access token.
I know this question has already been answered, but I'll post this answer here just in case someone still needs to do this. This method does not require user's consent. The below is Node.js / JS but can be easily translated into whatever language with the basics below.
// set default authentication for DocuSign; pulls data from this account
var auth = {
'Username': '(user email)',
'Password': '(user password)',
'IntegratorKey': '(api key found in admin)',
};
var options = {
'uri': 'https://www.docusign.net/restapi/v2/login_information',
'method': 'GET',
'body': '',
'headers': {
'Content-Type': 'application/json',
// turns the auth object into JSON
'X-DocuSign-Authentication': JSON.stringify(auth)
}
};
// send off your request using the options above
The above returns a response:
{
"loginAccounts": [
{
"name":"Your Company Name",
"accountId":"0000000",
"baseUrl":"https://{your_subdomain}.docusign.net/restapi/v2/accounts/0000000",
"isDefault":"true",
"userName":"User's Name",
"userId":"(36 character UUID)",
"email":"user#example.com",
"siteDescription":""
}
]
}
At this point, you can have to save the baseUrl and accountId that is returned. For the baseUrl, you only need to save the the sub-domain and domain url (https://{your_subdomain}.docusign.net), not the url paramters after that.
Now you can have enough information to make requests. The below example request pulls all the templates under this account.
var options = {
'uri': baseUri+'/accounts/'+accountId+'/templates',
'method': 'GET',
'body': '',
'headers': {
'Content-Type': 'application/json',
// turns the auth object into JSON
'X-DocuSign-Authentication': JSON.stringify(auth)
}
};
// send off your request using the options above

401 unauthorize exception for multitenant web api

Need help in authenticating the token request from any client application to WEB API. We have registered our web API has multi-tenant application in Azure AAD. My client application which has been registered in different tenant was able to get Access token from AAD.while making Http request to our endpoint with passing the access token part of request header, we are receiving 401 unauthorized exception. I found reason while browsing for multi tenant scenario is to disable ValidateIssuer and have custom handler.
• Is there any custom handler on implementing for WindowsAzureActiveDirectoryBearerAuthentication. I see people are using OpenIDConnect. But for WEB API, we are using WindowsAzureActiveDirectoryBearerAuthentication i.e Is there any equivalent Event for validation of access token in UseWindowsAzureActiveDirectoryBearerAuthentication and tell user is authenticated ?.
• Is there any better standard of validation of access token and tell user is valid user ?.
• Can we get the claims of user by passing bearer token to WEBAPI Authorize filter ?. or will httprequest object claims gets user information like given name, tenant name, object ID (esp. localhost debugging scenario also.), If we can get those information, we can have our own logic of validation.
Please let us know whether this approach is best practice for authenticating a user.
You could implement a custom issuer validator and assign it to the IssuerValidator property. This is useful when you can't specify a predefined list of issuers in configuration and need some runtime logic to determine if you trust the issuer presented in the token:
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
IssuerValidator = (issuer, token, tvp) =>
{
if (db.Issuers.FirstOrDefault(b => (b.Issuer == issuer)) == null)
return issuer;
else
throw new SecurityTokenInvalidIssuerException("Invalid issuer");
}
}
You could decode the access token to get basic user information like family_name/given_name , but you can only get that by using user identity to acquire the access token .

Azure delegated mail reading across office365 domain (nodejs)

Trying to read/write/update the emails of each user within the domain on office365. I'm using Azure Active Directory and an Azure app. I'm the account admin. I've turned on a whole load of permissions at this stage as not sure which ones are necessary.
I'm programming in Nodejs. So...
Authenticate my app, using OAuth, against Azure so that I can access my own application. <- WORKS.
Request admin rights against the account at this URL: https://login.microsoftonline.com/common/adminconsent?client_id='+clientID+'&redirect_uri'+uri where clientID and uri are replaced. This redirects me to authentication against Microsoft platform. You can see all the permissions I have requested (while debugging) here:
Once I accept the permissions it returns me to my app, where it requests a token from: https://login.windows.net/[my-app-tenant-id]/oauth2/token. That returns the access token. So far so good
After that, I then try and use this token to read a user of my organisations emails (for instance me, but there are a few users I have tried it with). It goes to: https://graph.microsoft.com/v1.0/[userEmailAddress#domain.com/messages. I pass the token as a bearer.
This is the problem, I get
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure.",
"innerError": {
"request-id": "[request-id]",
"date": "2017-03-21T17:31:24"
}
}
}
I have tried this with a couple of URLs, for instance the outlook API, with this url https://outlook.office.com/api/v2.0. I think however that is for simple OAuth and wouldn't work this way anyway.
If anyone can explain how to get the access token to be valid, would be great.
FYI, I am also, simultaneously testing out the npm library azure-graphapi and using:
var clientID = '[client id]'
var clientSecret = '[client secret]'
var tenant = '[tenant id]'
var graph = new GraphAPI(tenant, clientID, clientSecret);
graph.get('users/', function(err, user) {
resp.json({users: user});
})
I can get the list of users. So perhaps I am being stupid and somehow I can use this to read a users email?
Thanks
Maybe you have made a mistake in the request header to get access token.
As you can see, I got a token by sending a POST request to the https://login.microsoftonline.com/common/oauth2/v2.0/token endpoint and passed the value mail.read to the scope parameter in this request.
Now, I am able to get emails of the user with that token.

Resources