Postman + NTLM Authentication + Authorization with claims + ASP.NET Core API = 403 Forbidden - postman

We have an ASP.NET Core API that uses Windows Authentication and Claim based identity.
The API has one Controller with multiple Actions. The Actions have different authorization policies.
[Authorize(Policy = "Read")]
[HttpGet]
public async Task<ActionResult<Item>> Read()
{ ... }
[Authorize(Policy = "Write")]
[HttpPost]
public async Task<ActionResult<Item>> Write(Item item)
{ ... }
In Startup.cs we have this:
services.AddAuthorization(options => {
options.AddPolicy("Read", policy => policy.RequireClaim("OurReadType","OurReadValue"));
options.AddPolicy("Write", policy => policy.RequireClaim("OurWriteType","OurWriteValue"));
});
We also have a front end that consumes this API. Everything works fine when the front end application accesses our API. Users have only access to read actions if they have the read claim and the same goes for write actions. When a user that has only the read claim tries to call a write action they'll get a 401 Unauthorized. This is all expected behavior. No problems so far.
The problem starts when we try to access our API from Postman. ONLY from Postman do we get 403 Forbidden errors.
Postman is configured to use NTLM Authentication using my personal username and password. And my account has both read and write claims.
If we remove the [Authorize(Policy = "Read")] annotation from an action, we no longer get the 403 error when calling that action using Postman. This makes me think that the problem is somewhere with postman and claims based authorization.
Does anybody have an idea of what the problem is? I'm fairly new to claims based identity and to using Windows authentication to this extent. So any help is appreciated.

Related

AWS Amplify Auth.updateUserAttributes() clear localStorage/sessionStorage

I am writing a web-based app that uses AWS Cognito as the authentication service. I use 'aws-amplify' to implement the client-app.
I am using Auth.updateUserAttributes() to update a custom attribute of users on Cognito. However, I found that the call of this function would clear all the Cognito-related items, including idToken, refreshToken, and accessToken stored in localStorage. As a result, the web app behaves like sign-out.
Here is the code about Auth's configuration
Amplify.configure({
Auth: {
userPoolId: process.env.REACT_APP_AWS_COGNITO_USER_POOL_ID,
region: process.env.REACT_APP_AWS_COGNITO_REGION,
userPoolWebClientId: process.env.REACT_APP_AWS_COGNITO_APP_CLIENT_ID,
storage: window.localStorage,
authenticationFlowType: 'CUSTOM_AUTH',
},
});
and the code I wrote to update the user's attribute. (I followed the example code from the amplify docs https://docs.amplify.aws/lib/auth/manageusers/q/platform/js/#managing-user-attributes)
let user = await Auth.currentAuthenticatedUser();
console.log(JSON.stringify(localStorage)); // it outputs the localstorage with idToken,
// refreshToken, accessToken and other items
// start with 'CognitoIdentityServiceProvider'
const result = await Auth.updateUserAttributes(user, {
'custom:attributes_1': '123456789',
});
console.log(result); // OUTPUT: SUCCESS
console.log(JSON.stringify(localStorage)); // Only 'amplify-signin-with-hostedUI'.
// idToken, refreshToken, accessToken and
// other items were gone. No key, no value.
After the last line, I could not interact with the web page anymore. If I refreshed the web page, I found I had signed out and had to sign in again.
It was still the same if I changed the storage for Auth from localStorage to sessionStorage.
Here are my questions:
Is this kind of behavior normal? Does Auth.updateUserAttributes() leads to a force sign-out?
If it's true, is there any way to avoid a mandatory sign-out?
If it's not, what's wrong with my code or configuration? Or should I do some specific configuration for the Cognito service?
Thanks a lot!
Well, I figured it out after reading the source code of aws-amplify.
Behind the call of Auth.userUpdateAttributes, amplify will finally call CognitoUser.refreshSession(refreshToken, callback, clientMetadata) (https://github.com/aws-amplify/amplify-js/blob/f28918b1ca1111f98c231c8ed6bccace9ad9e607/packages/amazon-cognito-identity-js/src/CognitoUser.js#L1446). Inside this function, amplify sends an 'InitiateAuth' request to Coginito. If an error of 'NotAuthorizedException' happens, amplify calls clearCachedUser() that delete everything I mentioned in my question from the localStorage.
There was an error of 'NotAuthorizedException' happening and reported by the network work monitor of Chrome Browser'. I thought it was generated after the sign-out-like behavior. However, it turned out to be triggered because no deviceKey was passed to the request's parameters.
So the whole story was:
I set remember device options in Cognito;
I used 'CUSTOM_AUTH' as the authentication flow type;
When a user successfully signed in to my application, Cognito didn't give the client the deviceKey because of the 'CUSTOM_AUTH' authentication flow type.
When Auth.userUpdateAttributes() was called, CognitoUser.refreshSession() was called behind it. It attached no deviceKey to Cognito when it sent ana request to asked Cognito to refresh the token. The Cognito rejected the request with an error of 'NotAuthorizedException'. The CognitoUser.refreshSession() handled the error and called clearCachedUser() to delete the stored tokens and other info from the localStorage.
My final solution is to turn off the remember device option since I have to use 'CUSTOM_AUTH' as the authentication flow type according to my application's functional requirements.
According to https://aws.amazon.com/premiumsupport/knowledge-center/cognito-user-pool-remembered-devices/, the remember device function only works when the authentication flow type is set as 'USER_SRP_AUTH'.

Aws Cognito Bearer Token : Unable to obtain configuration from: 'System.String'

I am working on aws cognito with bearer token.
Below is the code to setup Bearer configuration.
I have written code to login via user name & password
I am able to logged in successfully and get the token
But when i am trying to access my authorise API it is throwing below error.
Could you please help me to resolve this?
Looks like your system is failing when trying to download OpenID Connect metadata. This will be a URL such as the following - so you should make sure your API is configured with a URL you can reach in the browser:
https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_qqJgVeuTn/.well-known/openid-configuration
The following type of code should work when validating Cognito tokens in .NET, if you add the Microsoft.AspNetCore.Authentication.JwtBearer library. Note that Cognito does not issue an Audience claim so you need to avoid validating it:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_qqJgVeuTn";
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = false,
};
});
Note that this includes a website library in APIs and expected OpenID Connect endpoints to exist. Personally I prefer not to write OAuth secured APIs like this.
JWKS ENDPOINT
The standard way to do API token validation is for the API to only know about the JWKS endpoint, as in these examples:
Node.js API JWT Validation
Java API JWT Validation
I found this approach a little harder in .NET, where libraries didn't quite do what I wanted. Here is some code that shows how to use extensibility points, in case useful:
.NET API JWT Validation

AWS Cognito authentication with Bearer token

I'm providing an external-facing REST GET API service in a kubernetes pod on AWS EKS. I had configured an ALB Ingress for this service which enforces Cognito user pool authentication. Cognito is configured with Authorization code grant with the openid OAuth scope enabled.
If I invoke my REST API from the browser, I get redirected to the Cognito login page. After a sucessful authentication on the form here, I can access my REST GET API just fine. This works, but this is not what I'd like to achieve.
Instead of this, I would need to use a Bearer token, after getting successfully authenticated. So first I invoke https://cognito-idp.ap-southeast-1.amazonaws.com using Postman with the request:
"AuthParameters" : {
"USERNAME" : "<email>",
"PASSWORD" : "<mypass>",
"SECRET_HASH" : "<correctly calculated hash>"
},
"AuthFlow" : "USER_PASSWORD_AUTH",
"ClientId" : "<cognito user pool id>"
}
and I get a successful response like:
"AuthenticationResult": {
"AccessToken": "...",
"ExpiresIn": 3600,
"IdToken": "...",
"RefreshToken": "...",
"TokenType": "Bearer"
},
"ChallengeParameters": {}
}
In the last step I'm trying to invoke my REST API service passing the Authorization HTTP header with the value Bearer <AccessToken> but I still get a HTML response with the login page.
How can I configure Cognito to accept my Bearer token for this call as an authenticated identity?
Quoting AWS support on this topic: "the Bearer token can not be used instead of the session cookie because in a flow involving bearer token would lead to generating the session cookie".
So unfortunately this usecase is not possible to implemented as of today.
STANDARD BEHAVIOUR
I would aim for a standard solution, which works like this:
API returns data when it receives a valid access token, or a 401 if the token is missing, invalid or expired - the API never redirects the caller
UIs do their own redirects to the Authorization Server when there is no token yet or when a 401 is received from the API
If it helps, my OAuth Message Workflow blog post demonstrates the 3 legged behaviour between UI, API and Authorization Server.
API GATEWAY PATTERN
It is perfectly fine to use an API Gateway Design Pattern, where token validation is done via middleware before hitting your API.
However that middleware must return a 401 when tokens are rejected rather than redirecting the API client.
IMPACT OF APIs REDIRECTING THE CLIENT
This may just about work for web UIs, though user experience will be limited since the UI will have no opportunity to save the user's data or location before redirecting.
For mobile / desktop apps it is more problematic, since the UI must redirect using the system browser rather than a normal UI view - see the screenshots on my Quick Start Page.
CHOICES
Any of these solutions would be fine:
Possibly the middleware you are using can be configured differently to behave like a proper API Gateway?
Or perhaps you could look for alternative middleware that does token validation, such as an AWS Lambda custom authorizer?
Or do the OAuth work in the API's code, as in this Sample API of mine
MY PREFERENCE
Sometimes I prefer to write code to do the OAuth work, since it can provide better extensibility when dealing with custom claims. My API Authorization blog post has some further info on this.

Django REST framework - prevent data access for user view?

In my api, I have a /users endpoint which currently shows (eg address) details of all users currently registered. This needs to be accessed by the (Ember) application (eg to view a user shipping address) but for obvious reasons I can't allow anyone to be able to view the data (whether that be via the browsable api or as plain JSON if we restrict a view to just use the JSONRenderer). I don't think I can use authentication and permissions, since the application needs to log a user in from the front end app (I am using token based authentication) in the first instance. If I use authentication on the user view in Django for instance, I am unable to login from Ember.
Am I missing something?
UPDATE
Hi, I wanted to come back on this.
For authentication on the Ember side I'm using Ember Simple Auth and token based authentication in Django. All is working fine - I can log into the Ember app, and have access to the token.
What I need to be able to do is to access the user; for this I followed the code sample here https://github.com/simplabs/ember-simple-auth/blob/master/guides/managing-current-user.md
I have tested the token based authentication in Postman, using the token for my logged in user - and can access the /users endpoint. (This is returning all users - what I want is for only the user for whom I have the token to be returned but that's for later!).
The question is how to do I pass the (token) header in any Ember requests, eg
this.store.findAll('user') .... etc
This is clearly not happening currently, and I'm not sure how to fix this.
UPDATE
Fixed it. Turns out that the authorize function in my application adapter was not setting the headers, so have changed the code to set the headers explicitly:
authorize(xhr) {
let { access_token } = this.get('session.data.authenticated');
if (isPresent(access_token)) {
xhr.setRequestHeader('Authorization', `Token ${access_token}`);
}
},
headers: computed('session.data.authenticated.token', function () {
const headers = {};
if (this.session.isAuthenticated) {
headers['Authorization'] = `Token ${this.session.data.authenticated.token}`
}
return headers;
})
Ember is framework for creating SPAs. These run in the browser. So for Ember to get the data, you have to send the data to the browser.
The browser is completely under the control of the user. The browser is software that works for them, not for the owner of the website.
Any data you send to the browser, the user can access. Full stop.
If you want to limit which bits of the data the user can read from the API, then you need to write the logic to apply those limits server-side and not depend on the client-side Ember code to filter out the bits you don't want the user to see.
I don't think I can use authentication and permissions, since the application needs to log a user in from the front end app (I am using token based authentication) in the first instance. If I use authentication on the user view in Django for instance, I am unable to login from Ember.
This doesn't really make sense.
Generally, this should happen:
The user enters some credentials into the Ember app
The ember app sends them to an authentication endpoint on the server
The server returns a token
The ember app stores the token
The ember app sends the token when it makes the request for data from the API
The server uses the token to determine which data to send back from the API

"Access token does not contain openid scope" in AWS Cognito

I am running a working AWS Cognito service on a frontend application which can successfully do the basic stuff - login, logout, signup, etc..
Right now I am trying to get user attributes through the backend API, such that:
1) The user login in the application and gets a JWT.
2) The JWT is being sent to the backend server.
3) The server has to extract the email of the user by using the access token
The closest thing that I found to what I need is this Cognito service.
So I am making a GET request to "https://mydomain.auth.eu-central-1.amazoncognito.com/oauth2/userInfo"
With Authorization Header as they are asking for, but I keep getting this response:
{
"error": "invalid_token",
"error_description": "Access token does not contain openid scope"
}
I have tried searching for this error but couldn't find any explanation about the error.
Thanks by advance
Erez, are you using a custom UI?
Because the custom UI uses flows that are completely separated from the OAuth2 ones (USER_SRP_AUTH, USER_PASSWORD_AUTH). Tokens that are released with these flows are not OpenID Connect compliant (basically they don't contain the openid scope) so you cannot use them to gather user infos (since the userinfo endpoint is OpenID Connect compliant and needs to be invoked with jwts compliant with OIDC standard).
We're also struggling on that, i'm sorry.
I had this exact problem and it was my fault. I was sending the id_token instead of access_token property of the token.
I program in PHP, so I was sending as header "Authorization: Bearer ".$token->id_token instead of "Authorization: Bearer ".$token->access_token. Now it works.
Hope it helps you or someone.
I am still experiencing the same issue. My problem relies on programmatic use of signIn service (not Hosted UI via federated login) in Amplify framework. After a long googling, I have discovered that this is because "openid" is not including in the scope of token. Only "aws.cognito.signin.user.admin" is included.
You can find a reference here, thread is still open https://github.com/aws-amplify/amplify-js/issues/3732
This solution seems to be fine for me How to verify JWT from AWS Cognito in the API backend?
If I understand correctly, you are successfully getting the #id_token sent to your front end from Cognito (steps 1-3). You can enable scopes on the #id_token by selecting the following options in your Cognito Pool App Client Settings:
I had a similar issue and I spent a couple of hours to find a solution. The access token you received it from cognito in your frontend application you need to send it to your backend then decode it and verify it. here is a good documentation from aws: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html