WebAPI 2 Create Custom Authentication Token - asp.net-web-api2

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.

Related

Custom Authorizationhandler for token evaluation that is done externally

When the user submits his credentials to my api, I call an external api to authenticate the user. After that, a token gets generated on the external api and will be sent to me. For that I implemented the HandleAuthenticateAsync function from the AuthenticationHandler:
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
//before this: make call to external api to get the access token
var claims = new[] {
new Claim(ClaimTypes.Name, submittedToken),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
I have implemented a custom AuthorizationHandler which I want to check for the access token that you got when you successfully authenticate. Note that the actual authentication and authorization is done by an external api which is a custom implementation. Here is the function:
public class IsAuthorizedRequirement : AuthorizationHandler<IsAuthorizedRequirement>, IAuthorizationRequirement
{
public AuthenticateHandlerHelperFunctions AuthenticateHandlerHelper;
public IsAuthorizedRequirement()
{
AuthenticateHandlerHelper = new AuthenticateHandlerHelperFunctions();
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IsAuthorizedRequirement requirement)
{
if(!context.User.HasClaim(c => c.Type == ClaimTypes.Name))
{
context.Fail();
return;
}
var token = context.User.FindFirst(c => c.Type == ClaimTypes.Name).Value;
if (!string.IsNullOrEmpty(token))
{
context.Fail();
return;
}
var checkedToken = await AuthenticateHandlerHelper.CheckAccessToken(token);
if (checkedToken == null)
{
context.Fail();
return;
}
context.Succeed(requirement);
}
}
The CheckAccessToken function makes a simple HTTP Post Request to the external Api where I get back if the token is still valid or not. Is this a valid implementation especially when multiple users are using this? Especially the claims that I use: Are they created for each user or will the content inside ClaimsType.Name be overwritten each time a user makes a request? Currently I have no way to test this so I just wanted to know if I am on the right track for this. Thanks
Is this a valid implementation especially when multiple users are using this?
I strongly stand against this approach. Implementation like this mean you would call external API for validate and generate token(or cookie or any form of authenticated certificate) on external server for each and any of your request(which require authentication).
It's could be consider acceptable if we have some special cases on just some endpoints. But for the whole API/Web server. Please don't use this approach.
Especially the claims that I use: Are they created for each user or will the content inside ClaimsType.Name be overwritten each time a user makes a request?
They'll create for each request. As I can see in the code there are no part for generate cookie or some form of retaining user information for the client to attach next request afterward.

How to add additional claims for MVC client with IdentityServer4

I'm using the IdentityServer4 "AspNetCoreAndApis" sample application found here
It has a token server and an MVC client application.
The identity server project has an external OIDC authentication provider set up using their demo server - https://demo.identityserver.io/
After hitting a protected endpoint in MvcClient, being redirected to the local identity server, choosing and authenticating with the demo server, it reaches the ExternalController callback of the local identity server. At this point I would like to issue additional claims to the user, and have them be available in MvcClient.
There's code in the callback to addadditionalLocalClaims and issue a cookie. I tried adding another claim:
var additionalLocalClaims = new List<Claim>();
additionalLocalClaims.Add(new Claim("TestKey", "TestValue"));
await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, localSignInProps, additionalLocalClaims.ToArray());
But by the time the user arrives in the HomeController of MvcClient this claim is not there.
I think I don't properly understand which authentication scheme is being used where, and the function of the relevant cookies.
EDIT:
In response to the first comment below, I tried attaching a claim to a requested scope, but still no luck - this is the in memory resource store:
public static IEnumerable<ApiResource> Apis
{
get
{
var apiResource = new ApiResource("api1", "My API");
apiResource.UserClaims.Add("TestKey");
var resources = new List<ApiResource>
{
apiResource
};
return resources;
}
}
The MvcClient is both allowed the api1 scope, and requests it.
Your client MVC could get the user's custom claims from ID token or UserInfo endpoint .
To add claims to ID token , you can set client's config :AlwaysIncludeUserClaimsInIdToken . But involve all user claims in ID token is not recommended concern about the size of ID Token .
A better solution is making your client app get user's claims from UserInfo endpoint :
public class MyProfileService : IProfileService
{
public MyProfileService()
{ }
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var claims = new List<Claim>()
{
new Claim("TestKey", "TestValue")
};
context.IssuedClaims.AddRange(claims);
return Task.CompletedTask;
}
public Task IsActiveAsync(IsActiveContext context)
{
// await base.IsActiveAsync(context);
return Task.CompletedTask;
}
}
Register in DI :
services.AddTransient<IProfileService, MyProfileService>();
The IProfileService service could be used to add claims to ID Token, Access token and UserInfo endpoint . By default the custom claims won't involve in ID Token event using IProfileService , the reason explained above - the ID token size . So you can make your client app get claims from UserInfo endpoint with OIDC middleware config :
options.Scope.Add("profile");
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.MapJsonKey("TestKey", "TestKey");
Above codes will add OIDC profile permission to get claims from endpoint , and send a request to connect/userinfo endpoint with ID Token , and get claims and map claim whose name is TestKey to your client's claim principle and save to cookie . Now you can get the claims with User.Claims in MVC .

Is is possible to disable authentication providers for specific routes?

We're evaluating service stack v.4.5.6.0 for a Web API and we want clients to be able to authenticate using basic auth or credentials but we do not want them to be able to provide a basic auth header in place of a JWT token or session cookie when using our services. While I realize this is somewhat arbitrary, is there a way to exclude routes from specific providers or force the use of a token/cookie to authenticate once they've logged in?
Auth config from AppHost:
private void ConfigureAuth(Container container)
{
var appSettings = new AppSettings();
this.Plugins.Add(new AuthFeature(() => new CustomAuthUserSession(),
new IAuthProvider[]
{
new CredentialsAuthProvider(),
new BasicAuthProvider(),
new JwtAuthProvider(appSettings)
}) { IncludeAssignRoleServices = false, MaxLoginAttempts = 10} );
var userRepository = new CustomUserAuthRepository(container.TryResolve<IDbConnectionFactory>());
container.Register<IAuthRepository>(userRepository);
}
ServiceStack lets you decide which AuthProviders you want your Services to be authenticated with, but it doesn't let you individually configure which adhoc AuthProviders applies to individual Services. Feel free to add this a feature request.
However if you want to ensure that a Service is only accessed via JWT you can add a check in your Services for FromToken which indicates the Session was populated by a JWT Token, e.g:
[Authenticate]
public class MyServices : Service
{
public object Any(MyRequest request)
{
var session = base.SessionAs<AuthUserSession>();
if (!session.FromToken)
throw HttpError.Unauthorized("Requires JWT Authentication");
//...
}
}
From v4.5.7 that's now available on MyGet you can also use the new session.AuthProvider property which indicates what AuthProvider was used to Authenticate the user, e.g:
public object Any(MyRequest request)
{
var session = base.SessionAs<AuthUserSession>();
if (session.AuthProvider != JwtAuthProvider.Name)
throw HttpError.Unauthorized("Requires JWT Authentication");
//...
}
Refer to the docs for different AuthProvider names for each AuthProvider.

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);
}
});
}
}

Where to hook into WCF Pipeline to extract credentials for UserNamePasswordValidator from incoming HTTP Request Headers

Can anyone point me to a suitable WCF Extension Point for hooking into the WCF Pipeline to extract credentials for UserNamePasswordValidator from the headers of an incoming HTTP REST Request?
Yes I know about all the funky stunts with Http Handlers etc. you can pull to somehow get Basic/Digest Auth working but since the client I'm working on will be strictly Javascript based I've opted for a simple model where the credentials are passed using two custom headers over an SSL pipe.
Update: I've managed to improve on this by using the approach described here. While this does not solves the problem described in my question, it gets rid of having to authenticate in a authorization policy since authentication is now handled by a custom AuthenticationManager, bypassing the UsernamePasswordValidator alltogether.
For the time being I've solved the problem by combining Authentication and Authorization in a custom Authorization Policy. I'd still rather find a way to hook into the normal UserNamePasswordValidator authentication scheme because an Authorization Policy is supposed to to Authorization not Authentication.
internal class RESTAuthorizationPolicy : IAuthorizationPolicy
{
public RESTAuthorizationPolicy()
{
Id = Guid.NewGuid().ToString();
Issuer = ClaimSet.System;
}
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
const String HttpRequestKey = "httpRequest";
const String UsernameHeaderKey = "x-ms-credentials-username";
const String PasswordHeaderKey = "x-ms-credentials-password";
const String IdentitiesKey = "Identities";
const String PrincipalKey = "Principal";
// Check if the properties of the context has the identities list
if (evaluationContext.Properties.Count > 0 ||
evaluationContext.Properties.ContainsKey(IdentitiesKey) ||
!OperationContext.Current.IncomingMessageProperties.ContainsKey(HttpRequestKey))
return false;
// get http request
var httpRequest = (HttpRequestMessageProperty)OperationContext.Current.IncomingMessageProperties[HttpRequestKey];
// extract credentials
var username = httpRequest.Headers[UsernameHeaderKey];
var password = httpRequest.Headers[PasswordHeaderKey];
// verify credentials complete
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
return false;
// Get or create the identities list
if (!evaluationContext.Properties.ContainsKey(IdentitiesKey))
evaluationContext.Properties[IdentitiesKey] = new List<IIdentity>();
var identities = (List<IIdentity>) evaluationContext.Properties[IdentitiesKey];
// lookup user
using (var con = ServiceLocator.Current.GetInstance<IDbConnection>())
{
using (var userDao = ServiceLocator.Current.GetDao<IUserDao>(con))
{
var user = userDao.GetUserByUsernamePassword(username, password);
...