How to pass basic auth header to REST endpoint after Forms Authentication sign in? - backbone.js

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.

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

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

React protect admin panel using JsonWebToken

I got a single page app written with React and ReactRouter and the frontend seems to be easy going.
But I have no idea how to design a backend which is only accessable by admins. On the server-side I use a NodeJS / Express RESTful API which uses JWT (JsonWebToken) for authentication.
Do you have any ideas?
You can use Express Middleware to check and validate JWT. If the JWT is valid then proceed with the API otherwise redirect to login. There are lots of articles available on ExpressJs site about middleware.
One great option is express-jwt
You import it, make a middleware, and place it before any protected route. If the token is valid, req.user will be set with the JSON object decoded to be used by later middleware for authorization and access control.
const jwt = require("express-jwt");
const auth = jwt({
secret: jwt_secret
});
router.get("/secret/:uid", auth, profileCtrl.secret);

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

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

Get user role in clear text along with JWT when using AngularJs, WebAPI and OAuth2

I am sing OAuth2 in WebAPI project. I am authenticating user request in OWIN middleware. On successfull authentication I am sending an JWT access token to client. Now I can validate subsequent request at server and use [Authorize(Roles="myRole")] attribute on Api Controllers.
But how can I show validate client content in AngularJs and show pages based on user role? I have JWT at client and no idea how to get user role out of it?
Is it a good approach to extract information from JWT?
You will need to parse that JWT and get the values out. You can do that with the help of the angular-jtw library.
1) Download the angular-jwt.min.js (https://github.com/auth0/angular-jwt)
2) put a dependecy of "angular-jwt" on the application module:
var app = angular.module("YOUR_APP", ["angular-jwt"]);
3) pass the jwtHelper to your service or controller or wherever it is that you wish to use it.
app.module.factory("YOUR_SERVICE", function(jwtHelper){
...
});
4) use the decodeToken method of the jwtHelper you passed in to decode your token
For example, the code below is parsing out the role object from a jwt that came back from my service endpoint. Upon succssful return from the server the role is extracted from the jwt and returned.
return $http.post(serviceEndPoints.tokenUrl, data, config)
.then(function (response) {
var tokenPayLoad = jwtHelper.decodeToken(response.data.access_token);
//Now do whatever you wish with the value. Below I am passing it to a function: (determineRole)
var userRole = determineRoles(tokenPayLoad.role);
return userRole;
});
};
Hope that helps
//Houdini
Currently we don't offer anything that would help you to take advantage of that information on the client. Also note: as today we do not validate the token on the client, we cannot really trust its content... while the [Authorize] attribute on the server side gets the role info only after the pipeline before it had a chance of validating the signature and deciding that the token is valid.
We might introduce something that will help with this scenario in the future, but for the time being you'd need to write custom code or rely on the server side to echo things back.

Resources