How does UseWindowsAzureActiveDirectoryBearerAuthentication work in validating the token? - c#

I am following the below GitHub sample for implementing Authentication mechanism across WebApp and WebApi.
https://github.com/AzureADSamples/WebApp-WebAPI-OpenIDConnect-DotNet
I am using a single App registration for both WebApp and WebApi, get a access token for "https://abc.onmicrosoft.com/App" and pass it on to WebApi. I am attaching the token to the HTTPS headers with the name "Bearer". I have the below in the WebApi Owin Startup class to validate the token for the Audience and Tenant, but does not actually validate the token for these as expected.
A couple of questions:
1. What triggers the below handler to validate the token for the tenant and audience? Is it the [Authorize] attribute on the Controller class?
2. How does it where to find the token to execute the handler?
3. Setting the SaveSigninToken to true saves the token. How can I retrieve the token and also Acquire access token for Graph API from this token?
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = "abc.onmicrosoft.com",
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = "https://abc.onmicrosoft.com/App",
SaveSigninToken = true,
}
});
Please advise. Thanks in advance!

What triggers the below handler to validate the token for the tenant and audience?
The middleware runs in Active mode by default, so it will attempt to find a token in every request. If it finds one, it will attempt to validate it. If it finds that it is valid, a ClaimsPrincipal is created which is accessible in further OWIN middleware and Web API components.
It also downloads the public keys with which it checks the token signature on app startup from Azure AD. You can see this if you use a tool like Fiddler.
How does it where to find the token to execute the handler?
I'm not sure if I am understanding this question, I hope my answer above clarified the process.
Setting the SaveSigninToken to true saves the token. How can I retrieve the token and also Acquire access token for Graph API from this token?
What you are trying to do is call an API using the on-behalf-of flow. You can find an example app here: https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof. More specifically this part should be of interest to you: https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof/blob/master/TodoListService/Controllers/TodoListController.cs#L133.
ClientCredential clientCred = new ClientCredential(clientId, appKey);
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as System.IdentityModel.Tokens.BootstrapContext;
string userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
string userAccessToken = bootstrapContext.Token;
UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
string userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(authority, new DbTokenCache(userId));
// In the case of a transient error, retry once after 1 second, then abandon.
// Retrying is optional. It may be better, for your application, to return an error immediately to the user and have the user initiate the retry.
bool retry = false;
int retryCount = 0;
do
{
retry = false;
try
{
result = await authContext.AcquireTokenAsync(graphResourceId, clientCred, userAssertion);
accessToken = result.AccessToken;
}
catch (AdalException ex)
{
if (ex.ErrorCode == "temporarily_unavailable")
{
// Transient error, OK to retry.
retry = true;
retryCount++;
Thread.Sleep(1000);
}
}
} while ((retry == true) && (retryCount < 1));

The [Authorize] decoration in the controller or whichever method we specify triggers the Owin security handler to validate the token and generates the claims.

Related

Ignore expired tokens on certain routes

Using JWT tokens, I was working on authentication for my .NET Core API. I was wondering if I can tell my API to ignore the expiry of my token when the user requests a certain route, like "/refresh" for example, to refresh expired tokens. I understand it can be worked around by supplying the tokens through a POST request, but I want the ability to ignore the expiry of a token when I only want to identify the calling user.
My code is done as below. Currently it works with a POST request but I want the ability to supply expired tokens through the header.
[AllowAnonymous]
[HttpPost("refresh")]
public async Task<IActionResult> Refresh(RefreshRequest refreshRequest)
{
// Validate refresh token
if (!_tokenService.RefreshTokenValid(refreshRequest.Refresh_Token)) {
return BadRequest("The request token is not a valid token.");
}
var tokenHandler = new JwtSecurityTokenHandler();
// Validate actual token
if (!tokenHandler.CanReadToken(refreshRequest.Token)) {
return BadRequest("The JWT token is not a valid token.");
}
// Get the user from the JWT token
var userId = _tokenService.GetUserIdFromtoken(refreshRequest.Token);
var repoUser = await _repo.GetUserById(userId);
// Map the user, generate a token and send response
var newToken = _tokenService.GenerateRefreshedJwtToken(repoUser);
var tokenUser = _mapper.Map(repoUser, new AuthRefreshedTokenUser(newToken));
return Ok(tokenUser);
}
A simple way is to create another authentication schema and set not to validate token's lifetime :
.AddJwtBearer(options =>
{
...
options.TokenValidationParameters.ValidateLifetime = false;
....
});
And apply that authentication scheme using [Authorize(AuthenticationSchemes = "schemeName")] on specific controllers/actions.

Force Azure Re-Authentication for each request in C#

I have a requirement where I want to fetch the Authorization Code for Azure login for each request in web API. As of now once the user signs in to Azure, after that I am not getting the authorization code as the user is already signed in.
How can I force the user to sign in again? This is the code I have been using as of now in the owin_startup file in web API?
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
CookieSecure = (CookieSecureOption)Convert.ToInt32(cookieSecure), // CookieSecureOption.NeverAlways
CookieManager = new SystemWebCookieManager(),
CookieHttpOnly = false,
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
RedirectUri = RedirectUri,
});
According to the code and the cases you post before, I think it it not about Azure ad b2c, so here I will give a reply for azure ad.
When you request an authorization code, there is a prompt=login property which is indicate the user should be prompted to reauthenticate.
Also here is an article about Forcing reauthentication with Azure AD which suggest use Token Max Age to achieve it.
You can append max_age= to the authorization URL (or just put 0 to force password authentication at all times). So once user gets redirected to the URL, they will be presented with an information to login again.
public class RequireReauthenticationAttribute : Attribute, IAsyncResourceFilter
{
private int _timeElapsedSinceLast;
public RequireReauthenticationAttribute(int timeElapsedSinceLast)
{
_timeElapsedSinceLast = timeElapsedSinceLast;
}
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
var foundAuthTime = int.TryParse(context.HttpContext.User.FindFirst(AppClaimTypes.AuthTime)?.Value, out int authTime);
if (foundAuthTime && DateTime.UtcNow.ToUnixTimestamp() - authTime < _timeElapsedSinceLast)
{
await next();
}
else
{
var state = new Dictionary<string, string> { { "reauthenticate", "true" } };
await context.HttpContext.Authentication.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties(state)
{
RedirectUri = context.HttpContext.Request.Path
}, ChallengeBehavior.Unauthorized);
}
}
}
It appears that the reason you wish to re-authenticate the user is to get/refresh the token in memory even when the auth cookie is present. Simplest way to achieve this would be decorate your controller with the AuthorizeForScopes attribute. You can check the sample project on Github here

Get accesstoken from azure in web with openidconnect

I have a desktop app where I authenticate users via Azure via AuthenticationContext.AcquireTokenAsync.
With the result from this method I can get the access-token, send it to my WCF and in my WCF use JwtSecurityToken / ConfigurationManager< OpenIdConnectConfiguration > to validate the token.
I've implemented login via Azure in a web app now by configuring it with app.UseOpenIdConnectAuthentication. So in my web app I dont explicitly call a method that returns a token. Rather I jack this in in asp.net's flow.
But now I want to fetch the token in a method and send it for validation similiarly how I did in my desktop app. I cannot find any token that the ConfigurationManager accepts however. I've looked in the regular HttpContext and Owincontext but no info that I find there is useful. Is the accesstoken stored anywhere where I can fetch it? Or do I have to do another request to get an accesstoken?
You should be getting access token as part of the response.
A simple way would be to look at the Authorization header. Look at code below -
HttpContext.Current.Request.Headers["Authorization"];
Also, I don't know what you mean by send the token for validation.
If you're trying to validate the token manually, here's a sample that does exactly that -
Manually validating a JWT access token in a web API
In the sample, specifically look at the Global.asax.cs
string jwtToken = null;
AuthenticationHeaderValue authHeader = request.Headers.Authorization;
if (authHeader != null)
{
jwtToken = authHeader.Parameter;
}
if (jwtToken == null)
{
HttpResponseMessage response = this.BuildResponseErrorMessage(HttpStatusCode.Unauthorized);
return response;
}
.........
.........
.........
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters = new TokenValidationParameters
{
// We accept both the App Id URI and the AppId of this service application
ValidAudiences = new[] { audience, clientId },
// Supports both the Azure AD V1 and V2 endpoint
ValidIssuers = new[] { issuer, $"{issuer}/v2.0" },
IssuerSigningKeys = signingKeys
};
try
{
// Validate token.
SecurityToken validatedToken = new JwtSecurityToken();
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwtToken, validationParameters, out validatedToken);

Return JWT Token generated by OAuthAuthorizatioServer from controller in Web API

Following #Taiseer Joudeh I was able to create simple POC of Web API. I'm able to create new account, then log-in and call secure Web API when I add JWT token to header.
I'd like to modify method that is responsible for creating accounts.
Right now I'm returning Create (201) code with new user object, but instead I'd like to return access token.
I've found similar question but it requires creating HttpClient and doing request to OAuthAuthorizatioServer TokenEndpointPath.
Second question I found requires generating temporary token that is returned to front-end, but then front-end must do additional request to server to get "real" token.
What I'd like to do is to return login response (access_token, token_type and expires_in) when I create user account.
I want user to be authenticated when his account is created.
I'm using just Web API and JWT without any cookies.
EDIT: My temporary solution:
after creating user I'm doing this:
var validTime = new TimeSpan(0, 0, 0, 10);
var identity = await UserManager.CreateIdentityAsync(user, "JWT");
var jwtFormat = new CustomJwtFormat(ApplicationConfiguration.Issuer);
var authenticationProperties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow, ExpiresUtc = DateTimeOffset.UtcNow.Add(validTime) };
var authenticationTicket = new AuthenticationTicket(identity, authenticationProperties);
var token = jwtFormat.Protect(authenticationTicket);
var response = new
{
access_token = token,
token_type = "bearer",
expires_in = validTime.TotalSeconds.ToInt()
};
return Ok(response);
where CustomJwtFormat comes from this awesome article.
Below is some code similar to what I'm doing in my application, which is using Asp.Net Core 1.0. Your signin and user registration will differ if you're not using Core 1.0.
public async Task<string> CreateUser(string username, string password)
{
string jwt = String.Empty;
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
var user = await _userManager.FindByNameAsync(username);
if (user == null) // user doesn't exist, create user
{
var newUser = await _userManager.CreateAsync(new ApplicationUser() { UserName = username }, password);
if (newUser.Succeeded) //user was successfully created, sign in user
{
user = await _userManager.FindByNameAsync(username);
var signInResult = await _signInManager.PasswordSignInAsync(user, password, false, true);
if (signInResult.Succeeded) //user signed in, create a JWT
{
var tokenHandler = new JwtSecurityTokenHandler();
List<Claim> userClaims = new List<Claim>();
//add any claims to the userClaims collection that you want to be part of the JWT
//...
ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.UserName, "TokenAuth"), userClaims);
DateTime expires = DateTime.Now.AddMinutes(30); //or whatever
var securityToken = tokenHandler.CreateToken(
issuer: _tokenOptions.Issuer, //_tokenAuthOptions is a class that holds the issuer, audience, and RSA security key
audience: _tokenOptions.Audience,
subject: identity,
notBefore: DateTime.Now,
expires: expires,
signingCredentials: _tokenOptions.SigningCredentials
);
jwt = tokenHandler.WriteToken(securityToken);
Response.StatusCode = (int)HttpStatusCode.Created;
await _signInManager.SignOutAsync(); //sign the user out, which deletes the cookie that gets added if you are using Identity. It's not needed as security is based on the JWT
}
}
//handle other cases...
}
}
Basically, the user is created and then signed in automatically. I then build a JWT (add in any claims you want) and return it in the response body. On the client side (MVC and Angular JS) I get the JWT out of the response body and store it. It is then passed back to the server in the Authorization header of each subsequent request. Authorization policies for all server actions are based on the set of claims supplied by the JWT. No cookies, no state on the server.
EDIT: I suppose you don't even need to call the signIn method in this process as you can just create the user, create a JWT, and return the JWT. When a user logs in on a future request you would use the Sign-In, create token, Sign-Out approach.
The idea of sending a response with access_token, token_type and expires_in, when the user is created is a great idea. But, I would stick to calling the "/token" end point with HttpClient to achieve this task. There are quite a few security checks that needs to be performed before token is generated. Since this is security I would not take any risk. I feel comfortable using the libraries/ code provided by industry experts.
That said, I tried to come up with a class you can call to create the token once the user is created. This code has been taken from the Microsoft's implementation of OAuth Authorization Server in their Katana project. You can access the source here. As you can see there is quite a lot happening when creating the token.
Here is the modified version of that middleware class for generating the token. You have to provide the proper OAuthAuthorizationServerOptions, Context, username, password, Scopes and clientid to get an access token. Please note that this is a sample implementation to guide you in the right direction. Please test it thoroughly if you want to use this.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AspNetIdentity.WebApi.Providers;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Microsoft.Owin.Security.OAuth;
namespace WebApi.AccessToken
{
public class TokenGenerator
{
public string ClientId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public IList<string> Scope { get; set; }
private OAuthAuthorizationServerOptions Options { get; } =
new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat("http://localhost:59822")
};
public async Task<IList<KeyValuePair<string, string>>> InvokeTokenEndpointAsync(IOwinContext owinContext)
{
var result = new List<KeyValuePair<string, string>>();
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
// remove milliseconds in case they don't round-trip
currentUtc = currentUtc.Subtract(TimeSpan.FromMilliseconds(currentUtc.Millisecond));
AuthenticationTicket ticket = await InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(owinContext, Options, currentUtc);
if (ticket == null)
{
result.Add(new KeyValuePair<string, string>("ERROR", "Failed to create acess_token"));
return result;
}
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(Options.AccessTokenExpireTimeSpan);
ticket = new AuthenticationTicket(ticket.Identity, ticket.Properties);
var accessTokenContext = new AuthenticationTokenCreateContext(
owinContext,
Options.AccessTokenFormat,
ticket);
await Options.AccessTokenProvider.CreateAsync(accessTokenContext);
string accessToken = accessTokenContext.Token;
if (string.IsNullOrEmpty(accessToken))
{
accessToken = accessTokenContext.SerializeTicket();
}
DateTimeOffset? accessTokenExpiresUtc = ticket.Properties.ExpiresUtc;
result.Add(new KeyValuePair<string, string>("access_token", accessToken));
result.Add(new KeyValuePair<string, string>("token_type", "bearer"));
TimeSpan? expiresTimeSpan = accessTokenExpiresUtc - currentUtc;
var expiresIn = (long)expiresTimeSpan.Value.TotalSeconds;
if (expiresIn > 0)
{
result.Add(new KeyValuePair<string, string>("expires_in", "bearer"));
}
return result;
}
private async Task<AuthenticationTicket> InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(IOwinContext owinContext, OAuthAuthorizationServerOptions options, DateTimeOffset currentUtc)
{
var grantContext = new OAuthGrantResourceOwnerCredentialsContext(
owinContext,
options,
ClientId,
UserName,
Password,
Scope);
await options.Provider.GrantResourceOwnerCredentials(grantContext);
return grantContext.Ticket;
}
}
}
Please let me know if you have any questions.
Thank you,
Soma.
I assume you are referring to the following article: http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity-2/
The general approach to authenticating a user in this case is
Make an HTTP call to the token endpoint
User Enters the credentials at the UI that is rendered
IdP verifies the credentials and issues a token
User makes another call to an authorized endpoint and OWIN will validate this (JWT) token (as configured in the ConfigureOAuthTokenConsumption method) and if successfull will set a user session with expiry same as the token expiry. The session is set using session cookies.
Now try to understand that in general, this whole process of authentication is required because your server does not trust the user logging in to be the user the person is claiming to be. However, in your case, you (or your server code) just created a user and you know for sure that the person accessing your website is the user you have just created. In this case you don't need to validate a token to create a session for this user. Just create a session with an expiry that suits your use-case.
The next time the user will have to log-in and prove him/herself to the server using a token but this time the user does not need to prove him/her self.
Note: If you absolutely are adamant about requiring a token to log in a user who you yourself have just created using their credentials here are a couple of issues.
You are taking on the responsibility of storing (having access to) the user credentials, which might not be with you over the lifetime of the application (in most cases you might want to act as a relying party rather than an IdP).
Even if you want to then doing it is not trivial. You will have to make the calls to the token end point in code (server or client side) on behalf of the user, enter their credentials for them, retrieve the token, call an authenticated endpoint on your site and retrieve the session cookie all while hiding all this from the user, which probably is something you will either do if you hate yourself :) but also isn't very secure way of doing things especially when you are taking all the trouble to implement OAuth in the first place.
Also, take a look at Windows Server 2016 (technical preview 5 at this time) which supports implicit grants and might take writing all this custom code off your plate if you can wait a bit for RTM.
In an OAuth solution you as a developer are are not required to handle the cookie setting yourself. The cookie handling is done for you automatically by the framework.
Also the only way to set a session is a. Using session cookies or b. Use cookie-less (in the url) methods. Look at http://www.cloudidentity.com/blog/2015/02/19/introducing-adal-js-v1/ for more details on token validation and session establishment (also search the term cookie and you will know what all it's used for).
If you start thinking about not using cookies at all not only will you have to figure out how to maintain session and do it securely without cookies but also have to re-write the token refresh code that detects and refreshes the token for you based on the presence of a session cookie. (i.e. not a smart idea)
I am using the exact technology stack and recently implemented token based authorization successfully. The link I took reference from had very neatly defined the token-based auth in Web APIs. A must bookmark page I must say. Here is the link: TOKEN BASED AUTHENTICATION IN WEB APIs.

How to validate the access token I'm receiving from WPF client in Web API?

I'm building two applications, WPF and Web API.
WPF connects to an identity server (now it's Azure AD) and get the access token then send it to my Web API to get the data.
How can I, in Web API, validate the access token to make sure it's correct. ?
Now I'm using Azure as I said but I should build to be able to validate any access token from any identity provider.
Is there an example or article explain this ?
Thanks
Encountered the same probelem.
I decided to use JWTToken
My architecture is the next one
Front <-> WebApi <-> Database
Front is in MVC4 WebApi2
The front will use a FormAuthentication method.
Save the JWT token once the user is fully logged, then send the Authentication Header on each request I do to the webapi.
The front part will only carry the encrypted JWT Token, nothing will be decrypted from it. Only send the token into the authentication http header tag.
On the webapi side each request are cached into DelegatingHandle, Check is the called method need to be authorized or not, validate the JWTToken then do what ever the webapi method does.
I cannot send you some part of my code because this is bellongs now to my company, but I can link you some Internet readings :)
1 - JWT
2- ASP.Net Web API with JWT (webapi handler)
You must then use the [Authorize] or [AllowAnonymous] tags on your webapi methods.
You can even create your own tag to handle all the Groups things.
If you have more questions, feel free to ask :)
I think this will answer to 99% of your security Questions.
I know this is one year old question, but I hope this answer will help other users :)
Client Side code
public async void Authenticate(string aadInstance, string tenant, string clientId, Uri redirectUri, string resourceId)
{
try
{
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
authContext = new AuthenticationContext(authority, new FileCache());
AuthenticationResult result = null;
try
{
result = await authContext.AcquireTokenSilentAsync (resourceId, clientId);
}
catch (AdalException ex)
{
if (ex.ErrorCode == AdalError.UserInteractionRequired || ex.ErrorCode == AdalError.FailedToAcquireTokenSilently)
{
result = await authContext.AcquireTokenAsync(resourceId, clientId, redirectUri, new PlatformParameters(PromptBehavior.Always));
}
}
ticket = result.AccessToken;
user = result.UserInfo.DisplayableId.Split('#')[0];
}
catch (Exception ex)
{
ticket = "Error";
throw ex;
}
}
Server side code
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
private JwtSecurityToken Validate(string token)
{
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKeys = config.SigningKeys, //.net core calls it "IssuerSigningKeys" and "SigningKeys"
ValidateLifetime = true
};
JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);
return jwt as JwtSecurityToken;
}

Categories