MVC with Angular - ValidateAntiForgeryToken fails after Azure Login redirect with Adal - angularjs

I have an MVC site with an embedded angular client and I've recently implemented an anti forgery XSRF token as a security measure.
I have set it up in Startup.cs as follows:
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
app.Use(next => context =>
{
if (string.Equals(context.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(context.Request.Path.Value, "/index.html", StringComparison.OrdinalIgnoreCase))
{
// We can send the request token as a JavaScript-readable cookie, and Angular will use it by default.
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
}
return next(context);
});
And I've implemented it within my angular front-end like so:
{ provide: XSRFStrategy, useFactory: xsrfFactory}
export function xsrfFactory(): CookieXSRFStrategy {
return new CookieXSRFStrategy('XSRF-TOKEN', 'X-XSRF-TOKEN');
}
And protecting my controllers like:
[Authorize] //Validation of AzureAD Bearer Token.
[ValidateAntiForgeryToken]
public class UserController : Controller
It is intended that the X-XSRF-TOKEN header be validated with any call to my API, and this works successfully for all calls in the original session. However, my app uses Adal to log the user in, and after the redirect from a successful login, this validation step fails and I receive a 400 from my API for any subsequent calls.
The original X-XSRF-TOKEN header is still sent with all outgoing requests from my angular client after the login so I suspect it must be that my server side no longer has the token to validate against, or my server has generated a new one and my client doesn't retrieve it. But for whatever reason it breaks down and it's very hard to debug without creating some custom filter so I can see what's going on inside it.
Is there a way to reset this token after a client side redirect so that both my server and client share common knowledge of it again? Or should I be generating the token in my Index.html for example?
EDIT
Edited controller decoration above for missing [Authorize] attribute.
So my controller is protected by a step validating the AzureAD Bearer token as well as the Anti-Forgery validation. Removing the AzureAD Validation as a test did not resolve the issue, oddly.
Error on failing API calls displays in output after Adal login as:
The provided anti-forgery token was meant for a different claims-based user than the current user.

Based on my understanding, you were protecting the controller using token. For this issue is expected, you can refer the progress of validate the anti-XSRF tokens from below(refer this link):
To validate the incoming anti-XSRF tokens, the developer includes a ValidateAntiForgeryToken attribute on her MVC action or controller, or she calls #AntiForgery.Validate() from her Razor page. The runtime will perform the following steps:
The incoming session token and field token are read and the anti-XSRF token extracted from each. The anti-XSRF tokens must be identical per step (2) in the generation routine.
If the current user is authenticated, her username is compared with the username stored in the field token. The usernames must match.
If an IAntiForgeryAdditionalDataProvider is configured, the runtime calls its ValidateAdditionalData method. The method must return the Boolean value true.
Since you were developing the SPA application with back-end web API, when the request to the web API, it will always issue the anti-XSRF token with no identity. And when you send the request to the back-end with anti-XSRF and Azure AD token, this time the web API already authenticate the request via the Azure AD token. And it will always return false when checking anti-XSRF token to match the identity information.
In this scenario, if the back-end only using the bear token authentication and store the token with session storage, there is no need to enable XSRF prevention since others is not able to steal the token and forge the request.
If your back-end also support the cookie authentication or basic auth, NTLM etc, you can disable the identity checking by adding the following to your Application_Start method: AntiForgeryConfig.SuppressIdentityHeuristicChecks = true.(refer this link)
More detail about XSRF/CSRF abouth oauth and web API, you can refer the threads below:
How does ValidateAntiForgeryToken fit with Web APIs that can be accessed via web or native app?
AntiForgeryToken does not work well with OAuth via WebAPI

Try replacing [ValidateAntiForgeryToken] with [AutoValidateAntiforgeryToken]
https://github.com/aspnet/Antiforgery/blob/dev/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenGenerator.cs

Related

How to validate AzureAD accessToken in the backend API

I just wanted to know how can we validate the azure ad access token in a backend API in my case i.e. Django rest framework.
Consider that I have a single page app or a native app and a backend API (django rest framework) completely independen of each other. In my case if my single page app/native app wants to access certain data from the backend API, and inorder to access the API, user should be logged in the backend API.
So what my approch is to make use of MSAL library to get the access token from the SPA/native app and then once token is acquired, pass that token to backend API, validate it, get the user info from graph api. If user exists in the DB then login the user and pass the required info. If user info doesn't exist then create the user, login and pass the info from the API.
So my question is when I pass the access token to my backend api, how can I validate that the token that a user/SPA/native app has passed to backend API is valid token or not?
Is it just we need to make an API call to graph API endpoint with accessToken that user/SPA/native passed and if it is able to get the user data with the accessToken then then token is valid or if it fails then the accessToken is invalid.
Is it the general way to validate the token or some better approach is there? Please help
Good day sir, I wanna share some of my ideas here and I know it's not a solution but it's too long for a comment.
I created a SPA before which used msal.js to make users sign in and generate access token to call graph api, you must know here that when you generate the access token you need to set the scope of the target api, for example, you wanna call 'graph.microsoft.com/v1.0/me', you need a token with the scope 'User.Read, User.ReadWrite' and you also need to add delegated api permission to the azure app.
So as the custom api of your own backend program. I created a springboot api which will return 'hello world' if I call 'localhost:8080/hello', if I wanna my api protected by azure ad, I need to add a filter to validate all the request if has a valid access token. So I need to find a jwt library to decode the token in request head and check if it has a token, if the token has expired and whether the token has the correct scope. So here, which scope is the correct scope? It's decided by the api you exposed in azure ad. You can set the scope named like 'AA_Custom_Impression', and then you can add this delegate api permission to the client azure ad app, then you that app to generate an access token with the scope of 'AA_Custom_Impression'. After appending the Bearer token in calling request, it will be filtered by backend code.
I don't know about python, so I can just recommend you this sample, you may try it, it's provided by microsoft.
I've solved the similar issue. I don't found how to directly validate access token, but you can just call graph API on backend with token you've got on client side with MSAL.
Node.js example:
class Microsoft {
get baseUrl() {
return 'https://graph.microsoft.com/v1.0'
}
async getUserProfile(accessToken) {
const response = await got(`${this.baseUrl}/me`, {
headers: {
'x-li-format': 'json',
Authorization: `Bearer ${accessToken}`,
},
json: true,
})
return response.body
}
// `acessToken` - passed from client
async authorize(accessToken) {
try {
const userProfile = await this.getUserProfile(accessToken)
const email = userProfile.userPrincipalName
// Note: not every MS account has email, so additional validation may be required
const user = await db.users.findOne({ email })
if (user) {
// login logic
} else {
// create user logic
}
} catch (error) {
// If request to graph API fails we know that token wrong or not enough permissions. `error` object may be additionally parsed to get relevant error message. See https://learn.microsoft.com/en-us/graph/errors
throw new Error('401 (Unauthorized)')
}
}
}
Yes we can validate the Azure AD Bearer token.
You can fellow up below link,
https://github.com/odwyersoftware/azure-ad-verify-token
https://pypi.org/project/azure-ad-verify-token/
We can use this for both Django and flask.
You can directly install using pip
but I'm not sure in Django. If Django install working failed then try to copy paste the code from GitHub
Validation steps this library makes:
1. Accepts an Azure AD B2C JWT.
Bearer token
2. Extracts `kid` from unverified headers.
kid from **Bearer token**
3. Finds `kid` within Azure JWKS.
KID from list of kid from this link `https://login.microsoftonline.com/{tenantid}/discovery/v2.0/keys`
4. Obtains RSA key from JWK.
5. Calls `jwt.decode` with necessary parameters, which inturn validates:
- Signature
- Expiration
- Audience
- Issuer
- Key
- Algorithm

Springboot + JWT +OAuth2 + AngularJS Stateless session

I am trying various Java Spring based security implementations as follows
1. JWT Authentication
User access /
Springboot identifies as protected resource and redirects user to /login
User enters credentials and browsers does a POST to /authenticate
Server validates the credentials and generates JWT token. Set into response header and redirects to /
Browser loads /. AngularJS recognizes JWT token in the response header and stores the same in the localStorage
All subsequent calls will have the Bearer token in header (injected through httpInterceptor)
Note: Stateless Session
2. OAuth2 authentication
User access /
Springboot identifies as protected resource and redirects user to /login
/login is intercepted by Spring security. Redirects to Oauth2 authorization server with a generated state and redirect URL back to application
User enters credentials
Oauth server redirects back to application URL "/login?code=xxx&state=yyy"
/login is intercepted by Spring security. Recognizes the code and state, generates Cookie and sets in response header. Redirects to /
Browser loads /. Browser recognizes cookie in the response header and stores the same.
If a call is made to /user, the Principal object is populated with the JWT which I am able to extract as follows
#RequestMapping(value= {"/user")
public ResponseEntity<Map<String, String>> user(Principal principal) throws Exception {
OAuth2Authentication obj = (OAuth2Authentication) principal;
authentication = obj.getUserAuthentication();
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) obj.getDetails();
String jwt = oAuth2AuthenticationDetails.getTokenValue();
All subsequent calls will have the Cookie in the Request
Note: A Stateful Session is created in server side to store the session details. This required to decrypt the cookie and identify the user
Now I want to implement security using Oauth2+JWT but stateless at same time as follows
3. OAuth2 + JWT + Stateless
User access /
Springboot identifies as protected resource and redirects user to /login
/login is interecepted by Spring security. Redirects to Oauth2 authorization server with a generated state and redirect URL back to application
User enters credentials
Oauth server redirects back to application URL "/login?code=xxx&state=yyy"
/login is intercepted by Spring security. Recognizes the code and state, extract JWT token by invoking
OAuth2AuthenticationDetails.getTokenValue() and set in response
header. Redirect to /
Browser loads /. AngularJS recognizes JWT token in the response header and stores the same in the localStorage
All subsequent calls will have the Bearer token in header (injected through httpInterceptor)
Question
I am trying to figure out how to implement the highlighted step above
Just an idea/direction, if I got you right:
You can create a GenericFilterBean and add that to the HttpSecurity filter chain.
When using JWT, there should be something similar (a filter, which extracts the bearer-token from the header) and then populates an Authentication object for Spring Security.
So the new filter could grab the token from the request and set the response accordingly.
You could also handle that in an unprotected (!) callback endpoint like login/callback?..., which than sets the cookie for you.
In our application, the server (spring boot) is totally stateless and does not have any oauth nor stateful stuff. Obviously it never redirects anything or has any other views/endpoints than / for AngularJS (and some REST-APIs under /api/...). Thus, the OAuth-flow is totally handled by AngularJS, which in turn checks the callback from the oauth-server and locally sets the JWT-Token (like in your first approach). In first versions we also tried to mix up redirects with stateless JWT and stateful sessions etc., but this led to very strange behavior with the logins - the state (logged in or not) was not always clear and in some cases redirects were wrong etc.
This might help you implement your desired solution.
The author proposes that once a user successfully authenticates with Oauth2 providers (Google etc), you send a short-lived token as a URL param to your frontend application and use this short-lived token to exchange it for a longer-lived token.

How to get user info with a valid Bearer Token?

At work we are making an SPFx Web Part React client app that deploys to SharePoint as a Web Part. Our back-end is a ASP.NET Core 2.2 Web API that is secured using Azure Portal's built in Authentication feature. The front-end is using AadHttpClient that magically handles the authentication by taking the context of the current page (SharePoint) that has the user already logged in. Doing so, silent authentication occurs and the API call is successfully made with authentication successfully passed. The AadHttpClient is supposed to magically bundle up the token in the request header that gets sent to the back-end Web API. I still need to debug the live development app and see how to retrieve the Bearer Token in the back-end Web API. These are my next probable steps?
Would I just probably use 'string bearerToken = Request.Headers.....;' or 'string bearerToken = Request.Headers["KeyValue"]' to get the token itself?
Assuming I can get this Bearer Token, how can I check the caller's user information? Is it just var userName = User.Identity.Name;? Or would I or could I use the token and some how make a call to Microsoft Graph API to view the user's info?
If you are using ASP.NET Core and using default authentication then things are bit easier. From documentation you can see that several tokens are injected in the request header based on Identity provider so in your case you have to look for following headers which Azure AD injects. These headers would contain ID Token which you would need to verify the claims and get user information.
X-MS-TOKEN-AAD-ID-TOKEN
X-MS-TOKEN-AAD-ACCESS-TOKEN
X-MS-TOKEN-AAD-EXPIRES-ON
X-MS-TOKEN-AAD-REFRESH-TOKEN
Ideally all the claims are injected automatically in ClaimsPrincipal
you can find more here
Official Docs
How To extract Token

AngularJS with spring security - general flow of authentication

Can anyone how look general flow of authentication in spring security, where frontend (so also login panel) is provieded by angularJS and node server ?
How should be these modules connected ?
I have been involved in three angularjs apps and in each of them we handled the whole user authentication/authorization in following way:
we defined a currentUser service which holds the information of current user of our application, things like user roles, email, name and ..., and this service have a very important method named isLoggedIn which based on this service values determine if user is logged in or not.
on the server side we have some sort of api which returns a token to a user after user have provided valid credentials, we post username/pass to the api and in response we receive a token and store it in a cookie or local sotrage. there are various ways of returning a token on would be using JWT(Json Web Token).
Tokens are sent in each request by help of $http interceptors, token may expire after 30 minutes which result in an unauthorized response from server.
for JWT refer to this link.
for client side authentication/authorization you can take a look at ng-book or this link.

How to pass basic auth header to REST endpoint after Forms Authentication sign in?

I have a Backbone/RequireJS application built on top of .NET MVC4. I am using Forms Authentication to authenticate the user against our back end data store, and this is working great.
Our services layer is a .NET Web Api Project (RESTful API), and is using a tokenized approach to auth (initial request includes basic auth header. If auth is successful, response payload includes authentication token. Subsequent requests pass the token).
What I'm not sure about is how to now authenticate against our services layer. I'm sure I'll have to pass the auth header, but not sure how to generate the token, as I won't have username/password in my JS.
Should I bypass Forms auth altogether here, and simply make an ajax request over SSL? I can POST to the /Account/Login action just as well, and use the Membership Provider to validate credentials. If successful, I can add the Auth header for initial request & use auth token for subsequent requests.
Example of adding auth header / custom token here:
$.ajaxSetup({
'beforeSend': function (xhr) {
if($.cookie("AuthToken")) {
xhr.setRequestHeader("CustomTokenHeader", $.cookie("AuthToken"));
} else {
var token = methodToBase64EncodeUsernamePassword(username, password);
xhr.setRequestHeader("Authentication", "Basic " + token);
}
}
});
Take a look at this solution, which is based off a screencast I cannot seem to find again. If I can find it, I will update the answer. You should be able to follow the solutions easily though. The one you are looking for is PerRouteMHOwnershipSample. I believe the best practice is to 'bypass Forms auth altogether here, and simply make an ajax request over SSL'. Any api route you want to secure, will be filtered and a token will then need to be passed from your app and decoded on the server. I would personally not look at using Forms Authentication to secure your api.

Resources