Validating Node.Js JWT token in asp.net/Authorize - asp.net

I am in the process of splitting up my asp.net service to multiple micro services. As a process, I have created my identity service using Node.Js and it uses JWT for tokens.
Now i want to use this token in C# so that all my [Authorise] attributes use this token and allow access.
I have looked at many implementations, but could not get this to work. Since JWT is a standard impementation, i do not understand a reason why this would not work.
This is my C# code
public void ConfigureAuth(IAppBuilder app)
{
var issuer = "myorg/identity2";
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
byte[] audienceSecret = TextEncodings.Base64Url.Decode
("xfecrrt7CV");
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
However, I get this error everytime i try to access a protected method.
{"Message":"Authorization has been denied for this request."}
Is there anything i am missing here? How do i add the claim identity to this?

Finally, it was resolved. One of my friends debugged the Identity source code and recommended to increased the key length. After increasing the key length, I was able to validate the token

Related

Azure Active Directory SSO with MSAL and openID Connect

I was tasked with writing an ASP.NET website that uses Azure Active Directory. I went with the route of OAuth and OpenID Connect. I am not able to use implicit flow and therefore must set the ResponseType to be code.
Using MSAL code samples I got most of it working but the problem is that all the samples are using a response type that returns tokens. I think I need to do it in 2 separate steps, first get the authorization code and then get the id token. I'm not exactly sure how to do this and would much appreciate some guidance here.
I have a startup class that look like this:
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions { });
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
Authority = authority,
ClientId = clientId,
RedirectUri = redirectUri,
Scope = "openid profile email offline_access user.readbasic.all", // a basic set of permissions for user sign in & profile access
ResponseType = OpenIdConnectResponseType.Code,
ClientSecret = clientSecret,
TokenValidationParameters = new TokenValidationParameters
{
// In a real application you would use ValidateIssuer = true for additional checks and security.
ValidateIssuer = false,
NameClaimType = "name",
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
}
});
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
// Handle any unexpected errors during sign in
context.OwinContext.Response.Redirect("/Error?message=" + context.Exception.Message);
context.HandleResponse(); // Suppress the exception
return Task.FromResult(0);
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
/*
The `MSALPerUserMemoryTokenCache` is created and hooked in the `UserTokenCache` used by `IConfidentialClientApplication`.
At this point, if you inspect `ClaimsPrinciple.Current` you will notice that the Identity is still unauthenticated and it has no claims,
but `MSALPerUserMemoryTokenCache` needs the claims to work properly. Because of this sync problem, we are using the constructor that
receives `ClaimsPrincipal` as argument and we are getting the claims from the object `AuthorizationCodeReceivedNotification context`.
This object contains the property `AuthenticationTicket.Identity`, which is a `ClaimsIdentity`, created from the token received from
Azure AD and has a full set of claims.
*/
IConfidentialClientApplication confidentialClient = GroupManager.Utils.MsalAppBuilder.BuildConfidentialClientApplication(null);
// Upon successful sign in, get & cache a token using MSAL
AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(new[] { "openid profile email offline_access user.readbasic.all" }, context.Code).ExecuteAsync();
}
How do I take the information from the result's tokens and create a claims identity for the AuthenticationTicket.Identity and access the user info?
Please note that this is an ASP.NET application. Not MVC and not Core.
If you use MSAL, you don't need to handle the code yourself. MSAL will return the token to you after you log in interactively, please see:Overview of Microsoft Authentication Library (MSAL).
Before that, you need to take a look at Add sign-in to Microsoft to an ASP.NET web app,the workflow is:
Code example please check: https://github.com/AzureAdQuickstarts/AppModelv2-WebApp-OpenIDConnect-DotNet
Update:
Try to enable ID token

Port over existing MVC user authentication to Azure functions

I have an old web application which is using ASP.net with the build in cookie based authentication which has the standard ASP.net SQL tables for storing the users credentials.
This is currently running as an Azure web app, but I was toying with the idea of trying to go serverless as per this example creating a ReactJs SPA hosting on blob storage to try and keep costs down and also improve performance without breaking the bank.
https://learn.microsoft.com/en-us/azure/architecture/reference-architectures/serverless/web-app
I was wondering if it is possible to port over the existing ASP.net authentication to Azure functions, to instead return a JWT (JSON Web Token) which could be passed back in the headers to handle authenticated requests.
When I have tried this in the past I have failed misserably, so I was wondering if anyone knows if it is possible?
I've seen this article, which seems to talk about Azure functions doing authentication, but with Azure AD, which I don't think is right for what I need.
https://blogs.msdn.microsoft.com/stuartleeks/2018/02/19/azure-functions-and-app-service-authentication/
The answer is kind of. What I mean by this is that you can use your existing database and many of the same libraries, but you can't port over the code configuration. The default authentication for Functions is either 1) The default API tokens or 2) one of the EasyAuth providers baked into App Services which is in the guide you linked. Currently, any other solution you'll need to setup yourself.
Assuming you go with the JWT option, you'll need to turn off all of the built-in authentication for Functions. This includes setting your HttpRequest functions to AuthorizationLevel.Anonymous.
At a basic level You'll need to create two things. A function to issue tokens, and either a DI service or a custom input binding to check them.
Issuing tokens
The Functions 2.x+ runtime is on .NET Core so I'm gong to borrow some code from this blog post that describes using JWTs with Web API. It uses System.IdentityModel.Tokens.Jwt to generate a token, which we could then return from the Function.
public SecurityToken Authenticate(string username, string password)
{
//replace with your user validation
var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);
// return null if user not found
if (user == null)
return null;
// authentication successful so generate jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
return tokenHandler.CreateToken(tokenDescriptor);
}
Validating Tokens
There are several guides out there for validating JWT within Azure Functions. I like this one from Ben Morris: https://www.ben-morris.com/custom-token-authentication-in-azure-functions-using-bindings/ (source code). It describes authenticating with either a custom input binding or with DI. Between the two, DI is the preferred option, unless there is a specific reason you need to use a binding. Here again, its the Microsoft.IdentityModel.JsonWebTokens and System.IdentityModel.Tokens.Jwt libraries that you'll need to do the bulk of the work.
public class ExampleHttpFunction
{
private readonly IAccessTokenProvider _tokenProvider;
public ExampleHttpFunction(IAccessTokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
[FunctionName("ExampleHttpFunction")]
public IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "example")] HttpRequest req, ILogger log)
{
var result = _tokenProvider.ValidateToken(req);
if (result.Status == AccessTokenStatus.Valid)
{
log.LogInformation($"Request received for {result.Principal.Identity.Name}.");
return new OkResult();
}
else
{
return new UnauthorizedResult();
}
}
}

How do I configure ASP.NET WebApi to validate bearer tokens against an OpenID Connect server?

I am writing a service which receives POSTs from another service, which includes an Authorization header containing a bearer token. This token is obtained independently from an OpenID Connect server (Keycloak in our dev environment, but not necessarily in production). Our service does not need to obtain or issue tokens; it merely needs to be able to validate them.
We are using .NET Framework 4.8 with self-hosted ASP.NET WebApi (OWIN 4, etc).
Configuration-wise, the information we have is:
the URL of the OpenID Connect service, eg. 'http://keycloak:8080/auth/realms/demo/'
the client ID, eg. 'js-client'.
The intent is that we obtain the issuer public key dynamically, from the OpenID server's metadata endpoint 'http://keycloak:8080/auth/realms/demo/.well-known/openid-configuration'. Currently I have something like:
WebApp.Start(startOptions, builder => {
var config = ...
// ... Set up routes etc ...
config.Filters.Add(new HostAuthenticationFilter("Bearer"));
builder.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "js-client",
Authority = "http://keycloak:8080/auth/realms/demo/",
RequireHttpsMetadata = false,
SignInAsAuthenticationType = "Bearer",
});
builder.UseWebApi(config);
}));
The controller action looks like:
[HttpGet]
[HttpPost]
[Authorize]
public IHttpActionResult Receive([FromBody] string dto) => Ok();
Currently, it always returns 401 Unauthorized with a message 'Authorization has been denied for this
request' irrespective of the validity of the token.
Wireshark reveals that our service never tries to contact the Keycloak server for OIDC metadata, so I guess that the authorisation handler is not even finding the token.
I've looked at UseJwtBearerAuthentication and UseOAuthAuthorizationServer too, but those seem to want more information than just an OIDC endpoint (unsurprising, really) or they need custom provider implementations.
This does not seem to be such an unusual use case that I need to implement my own validator, so presumably I'm missing something? Google searches turn up hundreds of examples which seem to relate only to ASP.NET Core or don't cover non-interactive use cases.
I managed to make progress on this by inspecting the source of OpenIdConnectAuthenticationMiddleware.
The JwtBearer middleware handles validation of the issuer, but needs to know the public key. Since I need to avoid configuring this directly, I need to ask the OIDC server for it.
This can be accomplished using a ConfigurationManager, which should deal with caching, etc for us:
private JwtBearerAuthenticationOptions GetJwtBearerTokenAuthenticationOptions(string issuer, IConfigurationManager<OpenIdConnectConfiguration> configurationManager)
{
return new JwtBearerAuthenticationOptions
{
Realm = "demo",
TokenValidationParameters = new TokenValidationParameters
{
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
// ... etc ...
IssuerSigningKeyResolver = (token, securitytoken, kid, validationparameters) =>
configurationManager.GetConfigurationAsync(CancellationToken.None).GetAwaiter().GetResult().SigningKeys,
ValidIssuer = issuer.TrimEnd('/'),
}
};
}
(The resolver delegate can't be async unfortunately, so I can't await this properly.)
The ConfigurationManager can be constructed like this (based on the internals of OpenIdConnectAuthenticationMiddleware):
private IConfigurationManager<OpenIdConnectConfiguration> GetOIDCConfigurationManager(string issuer)
{
var httpClient = new HttpClient(new WebRequestHandler());
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Demo OpenIdConnect middleware");
httpClient.Timeout = TimeSpan.FromMinutes(1);
httpClient.MaxResponseContentBufferSize = 10485760L;
var httpRetriever = new HttpDocumentRetriever(httpClient) { RequireHttps = false };
return new ConfigurationManager<OpenIdConnectConfiguration>($"{issuer}.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever(), httpRetriever);
}
These can then be used as follows:
const string issuer = "http://keycloak:8080/auth/realms/demo/";
var configurationManager = GetOIDCConfigurationManager(issuer);
builder.UseJwtBearerAuthentication(GetJwtBearerTokenAuthenticationOptions(issuer, configurationManager));
It all seems to work, although I'd very much like to know if there's a simpler way...?
Obviously, anyone using this in production should RequireHttps = true instead.

Identity Server: Access tokens/items set in AuthorizationProeperties in ExternalLoginCallback on the client

Question
I have an identity server implementation that is being used by a number of applications in test and production. I am currently working on a new feature, where the client application using the identity server can perform Azure service management REST api calls. For this, it needs a token. I can generate this token, store it and even access it in the AccountController in the identity server.
My issue is figuring out how to send this to the client. I don't think this token belongs in the claims for the user. So I tried to add it as part of AuthenticationProperties as a token, but I cannot seem to access it in the client. Should I store it in a session like this SO user did link? There is one answer to this question, but that does not seem right (I even tried it out of desperation!)
Relevant sections of code
Generate the token
var resource = "https://management.azure.com/";
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async context =>
{
// Acquire the token for the resource and save it
}
}
}
Restore it in AccountController
public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
{
string resource = "https://management.azure.com/";
// snip
result = await authContext.AcquireTokenSilentAsync(resource, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// snip
AuthenticationProperties props = null;
var tokens = new List<AuthenticationToken>();
var id_token = info.Properties.GetTokenValue("id_token");
if (id_token != null)
{
tokens.Add(new AuthenticationToken { Name = "id_token", Value = id_token });
}
if (result != null)
{
tokens.Add(new AuthenticationToken { Name = "management_token", Value = result.AccessToken });
}
if (tokens.Any())
{
props = new AuthenticationProperties();
props.StoreTokens(tokens);
}
// snip
// Can I access these "props" on the client? I even tried adding it to `Items`, no luck.
await HttpContext.Authentication.SignInAsync(user.UserId, user.DisplayName, provider, props, additionalClaims.ToArray());
}
So, my question, is this the right way go about it? If so, how do I access the authentication properties set? Or should I try saving this in the Session? If so, how do I store it in the client's session?
Any pointers would help. Thank you!
Just wanted to post an answer so that people wanting the same can benefit.
A token cache can be implemented to achieve this. This repository explains how.
Pay special attention to the AdalDistributedTokenCache linked here

How to authenticate an access token using OWIN OAuthBearerAuthentication?

What I want:
A token generator use OAuthAuthorizationServer and token consumer use OAuthBearerAuthentication (authenticate the access token).
Use OWIN pipeline to manage all stuff, token stuff and web api stuff.
What about the code:
public void Configuration(IAppBuilder app)
{
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthorizeEndpointPath = "/Authorize",
AllowInsecureHttp = true,
Provider = new OAuthAuthorizationServerProvider
{
OnGrantCustomExtension = GrantCustomExtension,
OnValidateClientRedirectUri = ValidateClientRedirectUri,
OnValidateClientAuthentication = ValidateClientAuthentication,
}
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new OAuthBearerAuthenticationProvider
{
//Handles applying the authentication challenge to the response message.
ApplyChallenge=MyApplyChallenge,
//Handles processing OAuth bearer token.
RequestToken=MyRequestToken,
//Handles validating the identity produced from an OAuth bearer token.
ValidateIdentity = MyValidateIdentity,
}
});
app.UseWebApi(new WebApplication3.Config.MyWebApiConfiguration());
}
What's the question:
The 3 properties of OAuthBearerAuthenticationProvider,
ApplyChallenge, RequestToken and ValidateIdentity. How to
implement the 3 methods?
In the token authetication process, What I thought is to decrypt the access token, validate the token from the client, and if the token is validated, put the identities of the token to the HttpContext.Current.User.
The OAuthBearerAuthenticationProvider's responsibility is to fulfill the
previous steps. Am I right?
As you know, UseOAuthAuthorizationServer has the job of authenticating the user. Then, UseOAuthBearerAuthentication has the job of ensuring that only authenticated users can access your application. Often, these two jobs are assigned to different web application. It looks like your application is doing both.
There are certainly some cases were you need to override the default OAuthBearerAuthenticationProvider. Maybe you do, or maybe you don't In my case, ApplicationCookie didn't quite fit the scenario. So, I'm storing a 3rd party JWT token in a cookie, rather than the header, and using it to indicate that the user is authenticated to a web application. I also needed to redirect to my own login page, rather than provide a 401.
Here's an implementation that does both:
public class CustomOAuthBearerProvider : IOAuthBearerAuthenticationProvider
{
public Task ApplyChallenge(OAuthChallengeContext context)
{
context.Response.Redirect("/Account/Login");
return Task.FromResult<object>(null);
}
public Task RequestToken(OAuthRequestTokenContext context)
{
string token = context.Request.Cookies[SessionKey];
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
return Task.FromResult<object>(null);
}
public Task ValidateIdentity(OAuthValidateIdentityContext context)
{
return Task.FromResult<object>(null);
}
}
I didn't need to do anything special in ValidateIdentity, but I needed to satisfy the interface.
To wire this up, tell your app to use JwtBearerAuthentication with your provider:
// controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AllowedAudiences = audiences.ToArray(),
IssuerSecurityTokenProviders = providers.ToArray(),
Provider = new CookieOAuthBearerProvider()
}
);

Resources