How to do token based auth using ServiceStack - authentication

How would I implement the following scenario using ServiceStack?
Initial request goes to http://localhost/auth having an Authorization header defined like this:
Authorization: Basic skdjflsdkfj=
The IAuthProvider implementation validates against a user store and returns a session token as a response body (JSON).
The client uses this token an resends it against the subsequent requests like http://localhost/json/reply/orders using the Authorization header like this:
Authorization: BasicToken <TokenFromPreviousSuccessfulLogin>
Using a AuthenticateAttribute I want to flag my Service to use Authentication.
How should I implement the validation of the token for the subsequent requests?
How should I implement the IAuthProvider to provide the token?
How would I register the Providers etc.? Using RequestFilters or using the AuthFeature?

ServiceStack's JWT AuthProvider and API Key AuthProvider both use token based Authentication.
Otherwise the BasicAuth Provider is a very simple class that simply extracts the UserName and Password from the BasicAuth Header and evaluates it:
public override object Authenticate(...)
{
var httpReq = authService.RequestContext.Get<IHttpRequest>();
var basicAuth = httpReq.GetBasicAuthUserAndPassword();
if (basicAuth == null)
throw HttpError.Unauthorized("Invalid BasicAuth credentials");
var userName = basicAuth.Value.Key;
var password = basicAuth.Value.Value;
return Authenticate(authService, session, userName, password, request.Continue);
}
If you want to provide enhanced behavior, I would simply inherit this class check for the Authorization: BasicToken header, if it exists use that otherwise call the base.Authenticate(...) method to perform the initial validation.

Related

Validate jwt token from ASP.NET Core controller when not using Authorize attribute

My client is sending the jwt token with some requests. Almost all requests is hitting a web api controller that is using the [Authorize] attribute. With this I am certain that my jwt token is properly validated, but for certain endpoints is really just want to grab the JWT token and get the sub value. I do this by using this extension method:
public static class HttpContextAccessorExtensions
{
public static string GetUserIdFromToken(this IHttpContextAccessor httpContextAccessor)
{
if (httpContextAccessor == null)
{
throw new ArgumentNullException(nameof(httpContextAccessor));
}
var context = httpContextAccessor.HttpContext;
string userId = null;
if (httpContextAccessor != null)
{
if (context != null)
{
var request = context.Request;
if (request != null)
{
request.Headers.TryGetValue("Authorization", out var bearer);
if (bearer.Any())
{
string t = bearer[0].Split(" ")[1];
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadToken(t) as JwtSecurityToken;
var utcNow = DateTime.UtcNow;
if (utcNow >= token.ValidFrom &&
utcNow <= token.ValidTo)
{
userId = token.Claims.FirstOrDefault(_ => _.Type.Equals("sub")).Value;
}
else
{
userId = String.Empty;
}
}
}
}
}
return userId;
}
}
My only problem here is that the jwt token isn't validated, so I am guessing that a "bad person" could just mingle with the valid to datetime, and keep extending the token lifetime. Please correct me if I am wrong on this.
What i want to know is: is there a way for me to validate the token? I know the JwtSecurityTokenHandler can call "ValidateToken", but this method needs a signing key, and I really dont know how to get this. I use IdentityServer 4 to generate tokens. Is there some easy way of injecting the Key into the IoC so I can get it, or is there an easy way to validate the token, that I dont know of?
Any help is appreciated
Since your access token is generated by IdentityServer4, then you should validate it using the IS4 Introspection Endpoint. This will give you the definitive answer as to if it is valid and whether or not it is still active.
Information about the Introspection Endpoint is in the IS4 docs at:
http://docs.identityserver.io/en/latest/endpoints/introspection.html
As referenced in those docs, perhaps the easiest way to interact with the endpoint is to use the IdentityModel client library (add package via NuGet), which adds the IntrospectTokenAsync() extension method to HttpClient and returns a TokenIntrospectionResponse object that has the information you need.
IdentityModel client Introspection Endpoint docs:
https://identitymodel.readthedocs.io/en/latest/client/introspection.html
Inside your controller you can just do this:
if(HttpContext.User.Identity.IsAuthenticated)
{
var claims = HttpContext.User.Claims;
}
and you can inspect the claims for whatever information is of interest.
that authenticated user identity is already validated and is available regardless of if the Authorize attribute is applied. If the user's cookie has expired etc that will return false. .
What i want to know is: is there a way for me to validate the token? I know the JwtSecurityTokenHandler can call "ValidateToken", but this method needs a signing key, and I really dont know how to get this. I use IdentityServer 4 to generate tokens.
When validating JWT token issued by Identity Server(using key pairs to issue/validate token) , specific for validating signature . API/resource server will pull down (and might cache) your identity providers discovery document located at OIDC endpoint : https://xxx/.well-known/openid-configuration. This document contains materials that allow the resource server to validate the token ,read available keys from jwks_uri .
Using key pairs menans the JWT token which is signed by IDS4 with private key. A JWT token is a non-encrypted digitally signed JSON payload which contains different attributes (claims) to identify the user/role. The signature is the last part of the JWT and needs to be used for verification of the payload. This signature was generated with the algorithm described in the header(RS256 for example) to prevent unauthorized access. In your api , you could use public key which published by IDS4(jwks_uri) to validate the signature of the JWT token . Please refer to this document for more details about JWT token .
You can use JwtBearerAuthentication middleware or IdentityServer.AccessTokenValidation middleware will help do that process . Code sample here is for your reference . If you want to manually validating a JWT token . Click here for code sample .
My only problem here is that the jwt token isn't validated, so I am guessing that a "bad person" could just mingle with the valid to datetime, and keep extending the token lifetime.
It's not recommended to use token without validating it . Since the signature part of JWT token is encrypted/encode use (header+payload) , even someone change the claims(expire time) , it won't pass the validation since he can't know Identity Server's private key to issue a correct signature .

MSAL: AcquireTokenSilentAsync always interacts with the endpoint

I'm lookig at MSAL and I'm trying to understand what's the correct way to use it in a client app. In my case, I'd like to authenticate the user and then use the id token against a "private" web api app.
Now, I was under the impression that AcquireTokenSilentAsync would reuse an existing token from the cache (when available) without performing an extra call to the authentication endpoint if the token was still valid and the requestes scopes could be satisfied (this was my interpretation and it probably is wrong). However, this seems not to be the case. What I'm seeing with fiddler is that this method will always access the authorization endpoint.
Initially, I thought that my client service wrappers should always cal this method in order to get the id token, which would then be passed to the backend web site through the authentication bearer header. Here's an example of what I mean:
public async Task<string> GetAllWorkers() {
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await GetToken());
var request = new HttpRequestMessage(HttpMethod.Get, _url);
var resposta = await _httpClient.SendAsync(request);
var content = await resposta.Content.ReadAsStringAsync();
return content;
}
GetToken is a method that wraps the typical code used for authenticating the user (uses a try/catch block for wrapping the AcquireTokenSilentAsync and, when that fails, redirects the user to the AcquireTokenAsync method for showing the login UI).
The question: is having this extra call before all my backend services really the way to go? Or should I cache the token and reuse it in all the internal web services call until I get a 401 (and only then should I call the GetToken method to refresh my id token?)
Editing to give more info
_clientApp = new PublicClientApplication(ClientId,
Authority,
TokenCacheHelper.GetUserCache());
TokenCacheHelper is the token cache helper that comes with most Azure AD samples. The GetToken method which returns the authentication header is a single liner that interacts with the helper that encapsulates the _clientApp field shown above:
return (await _helper.AuthenticateUser()).IdToken
And here is the AuthenticateUser method:
public async Task<AuthenticationResult> AuthenticateUser() {
try {
return await _clientApp.AcquireTokenSilentAsync(_scopes, _clientApp.Users.FirstOrDefault());
}
catch (MsalUiRequiredException ex) {
return await RetryWithGraphicalUI();
}
}
Now, the token cache helper is being hit. What I don't understand is why the AcquireTokenSilentAsync method ends up always calling the oauth2 endpoint (https://login.microsoftonline.com/{azure ad guid}/oauth2/v2.0/token)...
Meanwhile, I've changed the code making my helper class cache the AuthenticationResult. Now, AcquireTokenSilentAsync will only be called when one of the "internal" app's web api methods return 401 in response to a call performed with the bearer authorization header.
In the end, I've went along with caching the AuthenticationResult and it's ID Token. This seems to be the best option since it saves me a remote call. I'll only try to call AcquireTokenSilentAsync again when the web service returns 401.

Acquiring an Access token by using JWT used for AzureBearerAuthentication

I have a WebApi app that is using Windows Azure Active Directory Bearer Authentication to authenticate users. After the user is authenticated, I want to query Azure's Graph Api to get more information about the user.
I have a solution that works, but seems very hacky. I read the Authorization header and strip out the bearer part, and then I use AquireToken to get the new token:
var authHeader = HttpContext.Current.Request.Headers["Authorization"];
var tokenMatch = Regex.Match(authHeader, #"(?<=^\s*bearer\s+).+$", RegexOptions.IgnoreCase);
var result = authInfo.AuthContext.AcquireToken(resourceId, authInfo.Credential,
new UserAssertion(tokenMatch.Value));
return result.AccessToken;
There has to be a better way, but I've tried AcquireToken many different overloads and this was the only way I could get it to work. I tried AcquireTokenSilent, which works in my client app because there is a token in the TokenCache, but when I try in the WebApi, there doesn't seem anywhere to implement a TokenCache.
That is indeed somewhat hacky :-) see https://github.com/AzureADSamples/WebAPI-OnBehalfOf-DotNet for a way in which you can retrieve the incoming token through the ClaimsPrincipal. It boils down to passing TokenValidationParameters = new TokenValidationParameters{ SaveSigninToken = true } in the options and retrieving in from your controller or filter code via
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as System.IdentityModel.Tokens.BootstrapContext;

WebAPI 2 Create Custom Authentication Token

I want to create Custom Bearer Token, with some additional information to be store in the token.
Just want to Use Create Token functionality.(something like FormsAuthentication) without using default implementation(ASP.NET Identity) of User Tables.
1) Custom Login method(MyLogin), that will create custom bearer token with additional information(IP Address embedded into token).
2) on subsequent request be able to inspect the additional information and reject(treat the request as unauthenticated) if the additional information does not match some rule.
In case i receive the bearer token and find the request is coming from different IP address then the one embedded inside it, clear/Invalidate the Bearer Token and treat the current request as UnAuthenticated.
I'm by no means an expert but this is the information i gathered.
This seems to be relatively simple to do with ASP.NET Identity.
You need to create your own implementation of a token provider which implements the IAuthenticationTokenProvider interface. You implement the create method so it creates the token just the way you want and then you supply your provider when configuring the authentication middleware.
The configuration in your starup class would look something like this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
//Rest of code is here;
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/yourtokenendpoint"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider(),
AccessTokenProvider = new YourCustomTokenProvider() // YourCustomTokenProvider implements IAuthenticationTokenProvider
};
OAuthBearerAuthenticationOptions bearerOptions = new OAuthBearerAuthenticationOptions()
{
AccessTokenProvider = new YourCustomTokenProvider() // YourCustomTokenProvider implements IAuthenticationTokenProvider
}
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(bearerOptions);
}
}
I have never done this myself but I hope this was of some use.
EDIT: To validate the the token you could create a custom action filter that you decorate your controller actions with. In this action filter you could validate the token and do whatever you like with the request. See this guide.

Alternative to cookie based session/authentication

Is there an alternative to the session feature plugin in servicestack? In some scenarios I cannot use cookies to match the authorized session in my service implementation. Is there a possibility to resolve the session using a token in http header of the request? What is the preferred solution for that in case the browser is blocking cookies?
I'm using ServiceStack without the built-in auth and session providers.
I use a attribute as request filter to collect the user information (id and token), either from a cookie, request header or string parameter.
You can provide this information after the user takes login. You append a new cookie to the response and inject the id and token info on clientside when rendering the view, so you can use for http headers and query parameters for links.
public class AuthenticationAttribute : Attribute, IHasRequestFilter
{
public void RequestFilter(IHttpRequest request, IHttpResponse response, object dto)
{
var userAuth = new UserAuth { };
if(!string.IsNullOrWhiteSpace(request.GetCookieValue("auth"))
{
userAuth = (UserAuth)request.GetCookieValue("auth");
}
else if (!string.IsNullOrEmpty(request.Headers.Get("auth-key")) &&
!string.IsNullOrEmpty(request.Headers.Get("auth-id")))
{
userAuth.Id = request.Headers.Get("id");
userAuth.Token = request.Headers.Get("token");
}
authenticationService.Authenticate(userAuth.Id, userAuth.token);
}
public IHasRequestFilter Copy()
{
return new AuthenticationAttribute();
}
public int Priority { get { return -3; } } // negative are executed before global requests
}
If the user isn't authorized, i redirect him at this point.
My project supports SPA. If the user consumes the API with xmlhttprequests, the authentication stuff is done with headers. I inject that information on AngularJS when the page is loaded, and reuse it on all request (partial views, api consuming, etc). ServiceStack is powerful for this type of stuff, you can easily configure your AngularJS app and ServiceStack view engine to work side by side, validating every requests, globalizing your app, etc.
In case you don't have cookies and the requests aren't called by javascript, you can support the authentication without cookies if you always generate the links passing the id and token as query parameters, and pass them through hidden input on forms, for example.
#Guilherme Cardoso: In my current solution I am using a PreRequestFilters and the built-in session feature.
My workflow/workaround is the following:
When the user gets authorized I took the cookie and send it to the client by using an http header. Now the client can call services if the cookie is set in a http-header (Authorization) of the request.
To achieve this I redirect the faked authorization header to the cookie of the request using a PreRequestFilter. Now I am able to use the session feature. Feels like a hack but works for the moment ;-)
public class CookieRestoreFromAuthorizationHeaderPlugin : IPlugin
{
public void Register(IAppHost appHost)
{
appHost.PreRequestFilters.Add((req, res) =>
{
var cookieValue = req.GetCookieValue("ss-id");
if(!string.IsNullOrEmpty(cookieValue))
return;
var authorizationHeader = req.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.ToLower().StartsWith("basictoken "))
{
var cookie = Encoding.UTF8.GetString(Convert.FromBase64String(authorizationHeader.Split(' ').Last()));
req.Cookies.Add("ss-id",new Cookie("ss-id",cookie));
req.Items.Add("ss-id",cookie);
}
});
}
}