I am trying to make an web application that uses Jwt token authentication, logging on ect works fine.
Now I'm trying to add authorisation on who can access what pages and so on. On my controller I added [Authorize]. But when I'm logged in and try to go to one of the actions I always get a blank page and when I inspect it the action always returns 401 Unauthorized, any suggestions?
My headerservice for sending the authorization header to the Api:
using System.Collections.Generic;
namespace RelationizeWeb.Facade.Services
{
public class HeaderService : IHeaderService
{
private const string AuthorizationHeaderKey = "Authorization";
private const string BearerHeaderValue = "Bearer";
public Dictionary<string, List<string>> CreateAuthorizationHeader(string token)
{
var dict = new Dictionary<string, List<string>>
{
{ AuthorizationHeaderKey, new List<string>{ $"{BearerHeaderValue} {token}" } }
};
return dict;
}
public Dictionary<string, List<string>> CreateHeader(string key, string value)
{
var dict = new Dictionary<string, List<string>>
{
{ key, new List<string> { value } }
};
return dict;
}
}
}
Example of an Api request we make in for example our OpinionMakerService:
public IEnumerable<OpinionMaker> GetOpinionMakers(JwtTokenResponse jwt)
{
try
{
return (List<OpinionMaker>)_relationizeApiAgent.GetOpinionMakersWithHttpMessages(_headerService.CreateAuthorizationHeader(jwt.AccessToken)).Body;
}
catch(Exception)
{
return null;
}
}
You can use azure to generate the JWT for your application . You need to call an extension method named UseJwtBearerAuthentication in the StartUp.cs file for the REST api and use [Authorize] attribute to the controllers . The UseJwtBearerAuthentication method works like below
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Authority = <your auth name>,
Audience = <target audience URL>
});
Please see the links below for more reference
1.https://blogs.msdn.microsoft.com/webdev/2017/04/06/jwt-validation-and-authorization-in-asp-net-core/
https://pioneercode.com/post/authentication-in-an-asp-dot-net-core-api-part-3-json-web-token
you can use a client side library like ADAL.JS to call the azure service to obtain a JWT bearer token and then send the token in each request to controller. It will be verified in each request. We followed this approach in an Angular2 app with Azure JWT authentication.
Link for adal.js
https://github.com/AzureAD/azure-activedirectory-library-for-js
Resource :
https://blogs.msdn.microsoft.com/premier_developer/2017/04/26/using-adal-with-angular2/
edit : you can use the below code to access JWT Token and add it to the Authorization header before making a call to controller from another controller
HttpClient client = new HttpClient();
var token = <add your token here>; // call GetToken() method here and extract access_token from JwtTokenResponse class property.
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", token.Substring("Bearer ".Length).Trim());
//call the api method using SendAsync() or PostAsyc() etc.
Related
I'm using the following guide to setting up a default IdentityServer4 Angular project:
https://learn.microsoft.com/ja-jp/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-3.0
I want to make a request to a controller endpoint with this, and obtain the user's id (stored in the database) when doing that.
The quickest way to do that is apparently to put the user's id in the JWT token as a claim, I tried doing that using an IProfileService. It looks like this:
public class CustomClaimProfileService: IProfileService
{
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
private readonly UserManager<ApplicationUser> _userManager;
public CustomClaimProfileService(IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, UserManager<ApplicationUser> userManager)
{
_claimsFactory = claimsFactory;
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var subId = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(subId);
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
claims.Add(new Claim(JwtClaimTypes.Id, user.Id));
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
context.IsActive = true;
}
}
Adding the service as my last service in ConfigureServices like this:
services.AddTransient<IProfileService, CustomClaimProfileService>();
I wanted to use the AddProfileService<>() method I've seen reports of working better, but it's not working even when I put both using IdentityServer4 in my file and have the IdentityServer4 nuget package, so not sure what the problem is there.
I've seen reports of people saying AddTransient like that should work too if you put it at the end though.
Now trying to access the claim like this:
// POST: api/PracticeSession
[HttpPost]
public InitialPracticeRequestResult Post([FromBody] InitialPracticeRequest request)
{
var idClaim = User.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).FirstOrDefault();
if (idClaim != null)
{
return initialSessionRequestHandler.InitializePracticeSession(request, idClaim.Value);
}
else
{
throw new ArgumentException();
}
}
The user's claims are empty, I checked with the debugger and it seems GetProfileDataAsync is not getting called.
So now I read that before a claim actually gets put into the token, you need to list that claim as one of the claims that should be in the token when defining the resource, according to docs http://docs.identityserver.io/en/latest/reference/identity_resource.html .
This is the main question I have right now: how would I go about doing this for this template?
According to the ms docs above about the AddIdentityServerJWT() method called in Startup.cs:
AddIdentityServerJwt
This helper method configures a policy scheme for the app as the default authentication handler. The policy is configured to let Identity handle all requests routed to any subpath in the Identity URL space "/Identity". The JwtBearerHandler handles all other requests. Additionally, this method registers an <>API API resource with IdentityServer with a default scope of <>API and configures the JWT Bearer token middleware to validate tokens issued by IdentityServer for the app.
Now as far as I can see in IdentityServer4 docs, the claims to be put in the token for a resource need to be defined together with the resource itself. As far as I can see, this helper method already defined the resource for me, so then how do I define the claims I need in my token? Am I looking in the right place?
Thanks
I'm using Identity Server 3 to authenticate and generate Access/Refresh tokens for my angular Client.
I'm currently setting the Refresh Token to expire in 48 hours for my Angular Client.
Some users who use my Angular application will need to be Signed On for 100 days straight without having to re-enter their credentials, is it possible to set the expiration of my Refresh Token for a specific user only instead of the entire client?
I have 100 users in my database, I want just one specific user to not need to re-authenticate in 100 days while the rest should authenticate every 48 hours.
Something along the lines of:
if (user == "Super Man") {
AbsoluteRefreshTokenLifetime = TimeSpan.FromDays(100.0).Seconds,
}
Is this possible to achieve? or am I restricted to only setting the Refresh Token Expiration for the Entire Client?
Thank You
I've never worked with IdentityServer3 and I didn't test the code below, but I think the concept may work.
When I take a look at the code of IdentityServer3 then I can see that in DefaultRefreshTokenService.CreateRefreshTokenAsync the lifetime is set:
int lifetime;
if (client.RefreshTokenExpiration == TokenExpiration.Absolute)
{
Logger.Debug("Setting an absolute lifetime: " + client.AbsoluteRefreshTokenLifetime);
lifetime = client.AbsoluteRefreshTokenLifetime;
}
else
{
Logger.Debug("Setting a sliding lifetime: " + client.SlidingRefreshTokenLifetime);
lifetime = client.SlidingRefreshTokenLifetime;
}
You wouldn't want to change the core code, but you should be able to override the IRefreshTokenService with your own implementation.
When I take the code from CustomUserService sample as example:
internal class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/core", coreApp =>
{
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
var refreshTokenService = new MyDefaultRefreshTokenService();
// note: for the sample this registration is a singletone (not what you want in production probably)
factory.RefreshTokenService = new Registration<IrefreshTokenService>(resolver => refreshTokenService);
Where MyDefaultRefreshTokenService is a copy of the DefaultRefreshTokenService.
In order to make it compile add a NuGet package of IdentityModel (v1.13.1) and add the following class:
using System;
namespace IdentityServer3.Core.Extensions
{
internal static class DateTimeOffsetHelper
{
internal static Func<DateTimeOffset> UtcNowFunc = () => DateTimeOffset.UtcNow;
internal static DateTimeOffset UtcNow
{
get
{
return UtcNowFunc();
}
}
internal static int GetLifetimeInSeconds(this DateTimeOffset creationTime)
{
return (int)(UtcNow - creationTime).TotalSeconds;
}
}
}
Now there are some compilation errors concerning the events. You can remove the events in order to test the code. If it works you can always choose to add them.
And now for the implementation of the RefreshTokenLifetime per user. In your version of the RefreshTokenService you can remove the client code and use your own logic to determine the lifetime per user.
The subject is available, though I don't know if it already contains enough information. But if it does then you can access the userManager to read the lifetime from the store. Or use an alternative to pass the lifetime information (perhaps you can use a claim containing the lifetime value).
Again, I didn't test this, but I think the concept should work.
Considerations
Consider sliding sessions for example. With sliding sessions, you would send a new short-lived token with every authenticated action made by the user. As long as the user is active he will stay authenticated (e.g. it requires user interaction before expiration interval, although it requires token management implementations). If the user sends an expired token, it means he has been inactive for a while.
Let's see how JWT works:
The JWT is mainly suitable for the following cases:
In case of building API services that need to support
server-to-server or client-to-server (like a mobile app or single page app (SPA)) communication, using JWTs as your API tokens is a
very smart idea (clients will be making requests frequently, with
limited scope, and usually authentication data can be persisted in a
stateless way without too much dependence on user data).
If you’re building any type of service where you need three or more
parties involved in a request, JWTs can also be useful.
if you’re using user federation (things like single sign-on and
OpenID Connect), JWTs become important because you need a way to
validate a user’s identity via a third party.
more clarification at stop using jwts as session tokens
So Stop using JWT for sessions, it’s a bad idea to use JWTs as session tokens for most of cases.
Possible Solution
For Refreshing JWT, the JWT refresh tokens and .NET Core may be useful to implement your own code And descriptions inside JWT (JSON Web Token) automatic prolongation of expiration guides you to design a working scenario. You need to inspect desired user before refreshing operation.
I found another implementation at Handle Refresh Token Using ASP.NET Core 2.0 And JSON Web Token for you, maybe useful.
I'm not familiar with Microsoft's Identity Server (the "Identity Service" I refer to in the code below is a custom implementation), but you could consider writing an authentication handler to intercept the token in HTTP headers, examine a token prefix, then decide whether to process normally or allow an extended lifetime.
In my case, I intercept the token prior to JWT processing it. (I had to do this to get around a SharePoint workflow limitation. Oh, SharePoint.) Here's the AuthenticationHandler class:
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace CompanyName.Core2.Application.Middleware
{
[UsedImplicitly]
public class AuthenticationHandler : AuthenticationHandler<AuthenticationOptions>
{
public const string AuthenticationScheme = "CompanyName Token";
[UsedImplicitly] public const string HttpHeaderName = "Authorization";
[UsedImplicitly] public const string TokenPrefix = "CompanyName ";
public AuthenticationHandler(IOptionsMonitor<AuthenticationOptions> Options, ILoggerFactory Logger, UrlEncoder Encoder, ISystemClock Clock)
: base(Options, Logger, Encoder, Clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue(HttpHeaderName, out StringValues authorizationValues))
{
// Indicate failure.
return await Task.FromResult(AuthenticateResult.Fail($"{HttpHeaderName} header not found."));
}
string token = authorizationValues.ToString();
foreach (AuthenticationIdentity authenticationIdentity in Options.Identities)
{
if (token == $"{TokenPrefix}{authenticationIdentity.Token}")
{
// Authorization token is valid.
// Create claims identity, add roles, and add claims.
ClaimsIdentity claimsIdentity = new ClaimsIdentity(AuthenticationScheme);
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, authenticationIdentity.Username));
foreach (string role in authenticationIdentity.Roles)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
}
foreach (string claimType in authenticationIdentity.Claims.Keys)
{
string claimValue = authenticationIdentity.Claims[claimType];
claimsIdentity.AddClaim(new Claim(claimType, claimValue));
}
// Create authentication ticket and indicate success.
AuthenticationTicket authenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), Scheme.Name);
return await Task.FromResult(AuthenticateResult.Success(authenticationTicket));
}
}
// Indicate failure.
return await Task.FromResult(AuthenticateResult.Fail($"Invalid {HttpHeaderName} header."));
}
}
}
Then in the Startup class of your service, add code to decide which authentication handler to use. The key feature here is the ForwardDefaultSelector:
public void ConfigureServices(IServiceCollection Services)
{
// Require authentication token.
// Enable CompanyName token for SharePoint workflow client, which cannot pass HTTP headers > 255 characters (JWT tokens are > 255 characters).
// Enable JWT token for all other clients. The JWT token specifies the security algorithm used when it was signed (by Identity service).
Services.AddAuthentication(AuthenticationHandler.AuthenticationScheme).AddCompanyNameAuthentication(Options =>
{
Options.Identities = Program.AppSettings.AuthenticationIdentities;
Options.ForwardDefaultSelector = HttpContext =>
{
// Forward to JWT authentication if CompanyName token is not present.
string token = string.Empty;
if (HttpContext.Request.Headers.TryGetValue(AuthenticationHandler.HttpHeaderName, out StringValues authorizationValues))
{
token = authorizationValues.ToString();
}
return token.StartsWith(AuthenticationHandler.TokenPrefix)
? AuthenticationHandler.AuthenticationScheme
: JwtBearerDefaults.AuthenticationScheme;
};
})
.AddJwtBearer(Options =>
{
Options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Program.AppSettings.ServiceOptions.TokenSecret)),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(_clockSkewMinutes)
};
});
Add an extension method to the AuthenticationBuilder class:
public static AuthenticationBuilder AddCompanyNameAuthentication(this AuthenticationBuilder AuthenticationBuilder, Action<AuthenticationOptions> ConfigureOptions = null)
{
return AuthenticationBuilder.AddScheme<AuthenticationOptions, AuthenticationHandler>(AuthenticationHandler.AuthenticationScheme, ConfigureOptions);
}
And authentication options if you need them.
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
namespace CompanyName.Core2.Application.Middleware
{
public class AuthenticationOptions : AuthenticationSchemeOptions
{
[UsedImplicitly]
public AuthenticationIdentities Identities { get; [UsedImplicitly] set; }
public AuthenticationOptions()
{
Identities = new AuthenticationIdentities();
}
}
}
AuthenticationIdentities is just a class I define to associate a token with a username, roles, and claims (the token for the SharePoint workflow engine). It's populated from appsettings.json. Your options class most likely would contain a list of users who are authorized for an extended lifetime.
using System.Collections.Generic;
using JetBrains.Annotations;
namespace CompanyName.Core2.Application.Middleware
{
public class AuthenticationIdentity
{
public string Token { get; [UsedImplicitly] set; }
public string Username { get; [UsedImplicitly] set; }
[UsedImplicitly] public List<string> Roles { get; [UsedImplicitly] set; }
[UsedImplicitly] public Dictionary<string, string> Claims { get; [UsedImplicitly] set; }
public AuthenticationIdentity()
{
Roles = new List<string>();
Claims = new Dictionary<string, string>();
}
}
}
I create a ASP.NET WebApi project, it use [Authorize] attribute to authorize controllers, such as:
[System.Web.Http.Authorize(Roles="Users, Admins")]
public ValueController : ApiController
{
[HttpGet]
public string GetValue()
{
return "Hello World !";
}
}
now I want to add Signalr to this project. This is what I did:
In App_Start/Startup.Auth.cs, add following codes in ConfigureAuth() function to start SignalR.
app.Map(signalr, map =
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
};
hubConfiguration.EnableDetailedErrors = true;
map.RunSignalR(hubConfiguration);
});
Then I add a MessageHub, trying to allow authorized user to send messages to all clients:
[Microsoft.AspNet.SignalR.Authorize]
public class MessageHub : Hub
{
public void SendMessage(string message)
{
Clients.All.SendMessage(message);
}
}
if I start this project, type http://localhost:4000/signalr/hubs in browser, I can receive a hubs js.
Q1 - For me that means SignalR is working on my WebApi project, right?
now I need to test SendMessage, first thing I did is login through http://localhost:4000/Token, then get an access_token. In webApi I usually put this token in Header:
Authorization : Bearer access_token
to access APIs.
Q2 - now what should I do with this token to access MessageHub?
I've built a RESTful API (using ASP.NET Web API 2) which is only meant to be consumed from a single end-point. This end-point is a basic front-end site containing only HTML/CSS/JS. Due to various reasons, the front-end site and the API are completely external from one-another, with the front-end site being whitelisted in the API's CORS configuration.
I'm now trying to lock-down the API so that it's only accessible from this particular end-point, without introducing a new login system, because the context of where this page lives ensures that anyone accessing it is already a trusted user (it's technically behind a login system, but the page consuming the API has almost no knowledge of this context).
At a high level, I'd like to introduce a statically defined API Key of some sort, that would be hardcoded into both the API and the JavaScript of the consuming page, to help ensure that it's the only end-point accessing the API. We can assume that all communications between the front-end page and the API will be over a secure SSL/TLS connection.
My question: for such a case where I want to authenticate API requests from a particular page with a statically-defined API Key, what would be my best option from an ease-of-implementation standpoint? Most of the articles that I've found on Web API Authorization pivot around a user login system and seem grossly over-engineered for my particular use-case. I'd consider myself a novice when it comes to the subject and so I'm really just hoping for someone to point me in the right direction.
Thanks!
It seems like you are looking for a global filter in this specific case.
An authentication filter is a component that authenticates an HTTP request
You would basically send the shared / static api key with every request in the Authorization header and the custom filter would process this and decide whether the request is valid or not.
A basic implementation of the filter:
public class ApiKeyAuthenticationAttribute : IAuthenticationFilter
{
public bool AllowMultiple { get; set; }
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
// Get Auth header
AuthenticationHeaderValue authorization = request.Headers.Authorization;
// Validate the static token
if (authorization?.Parameter == "123")
{
IPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new Claim("CLAIMTYPE", "CLAIMVALUE") }));
context.Principal = principal;
}
else
{
context.ErrorResult = new AuthenticationFailureResult(request);
}
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
var challenge = new AuthenticationHeaderValue("Basic");
context.Result = new AddChallengeOnUnauthorizedResult(challenge, context.Result);
return Task.FromResult(0);
}
}
And to enable it for all calls to your api add it to your WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Some more config here
config.Filters.Add(new IdentityBasicAuthenticationAttribute());
}
}
The AuthenticationFailureResult and AddChallengeOnUnauthorizedResult are implementations of IHttpActionResult. For comprehensiveness I will add them here.
AuthenticationFailureResult
class AuthenticationFailureResult : IHttpActionResult
{
private HttpRequestMessage _request;
public AuthenticationFailureResult(HttpRequestMessage request)
{
_request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.RequestMessage = _request;
response.Content = new StringContent("ACCESS DENIED MESSAGE");
return Task.FromResult(response);
}
}
AddChallengeOnUnauthorizedResult
class AddChallengeOnUnauthorizedResult : IHttpActionResult
{
public AddChallengeOnUnauthorizedResult(AuthenticationHeaderValue challenge, IHttpActionResult innerResult)
{
Challenge = challenge;
InnerResult = innerResult;
}
public AuthenticationHeaderValue Challenge { get; private set; }
public IHttpActionResult InnerResult { get; private set; }
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = await InnerResult.ExecuteAsync(cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
// Only add one challenge per authentication scheme.
if (!response.Headers.WwwAuthenticate.Any((h) => h.Scheme == Challenge.Scheme))
{
response.Headers.WwwAuthenticate.Add(Challenge);
}
}
return response;
}
}
This code is from or a derivative of this article Authentication Filters in ASP.NET Web API 2 and this article Authentication Filters in ASP.NET Web API 2
I want to allow two types of authentication on my site :
* Forms authentication: The user login using his/her details in the form. The authentication should be made using cookies.
* Bearer: When calling WebAPI's (for mobile), the authentication should be made only by using bearer tokens.
I've relayed on the SPA template and some questions in SO and did successful made it available.
The only problem I'm facing is the ClaimsIdentity: I wish to use custom identity class. However, I'm being able to do so only in forms authentication, not in bearer WebAPI requests.
My custom identity:
public class MyIdentity : ClaimsIdentity, IMyIdentity
{
#region IMyIdentity
private Account _account = null;
public Account Account
{
get
{
if (_account == null)
{
if (this.IsAuthenticated)
{
Guid claimedAccountId = Guid.Parse(this.FindFirst(ClaimTypes.NameIdentifier).Value);
var accountService = ServiceLocator.SharedInstance.GetInstance<IAccountService>();
_account = accountService.Where(
a => a.Id == claimedAccountId
).FirstOrDefault();
}
_account = _account ?? Membership.Account.GuestAccount;
}
return _account;
}
}
#endregion
}
In Global.asax, I've overridden the Application_OnPostAuthenticateRequest method in order to set the custom identity, and it does working good - but only in forms, not in WebAPI.
In addition, I do set in WebApiConfig.cs
config.SuppressDefaultHostAuthentication();
so it does make sense that MyIdentity being nulled and User.Identity resets back to ClaimsIdentity.
So to sum up my question - is there a way to define which Identity class will be used, so I can set MyIdentity instead of ClaimsIdentity?
For Web API, you could try hooking into the OWIN authentication pipeline, and implement your own Authentication Filter, and use it to change the current principal to your own:
public class MyAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter
{
public Task AuthenticateAsync(HttpAuthenticationContext context, System.Threading.CancellationToken cancellationToken)
{
if (context.Principal != null && context.Principal.Identity.IsAuthenticated)
{
CustomPrincipal myPrincipal = new CustomPrincipal();
// Do work to setup custom principal
context.Principal = myPrincipal;
}
return Task.FromResult(0);
}
And register the filter:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new MyAuthenticationFilter());
...