We need to develop a "single sign on" service (SSO) to issue JWT for numerous amount of clients. Developers of these clients will also need an ability to validate these tokens. Obviously we can't provide them with our secret key we used to generate these tokens. So instead we decided to provide them an API service with two methods. One for issuing token and the second one to validate it.
I'm questioning myself if we're going for the right approach. Here is a basic scheme which shows how users will be working with their clients (secure applications)
User signs on with his credentials via our service and gets his access token.
Then his token is used in request headers of the secure application. SSO client module is AuthenticationHandler which sends HTTP requests to our service to check validity of the token.
Here is some code from SSO client module we use to validate the token. We use custom authentication handler which makes calls to the remote SSO service:
internal class SsoAuthenticationHandler : AuthenticationHandler<SsoAuthenticationOptions>
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!TryRetrieveToken(Request, out var token))
{
return AuthenticateResult.NoResult();
}
if (await _ssoClient.ValidateTokenAsync(token))
{
return AuthenticateResult.Success(...);
}
return AuthenticateResult.NoResult();
}
}
and the SsoClient iself:
public class SsoClient
{
public async Task<bool> ValidateTokenAsync(string token)
{
const string validateUrl = "api/auth/validatetoken";
var address = $"https://{_ssoHost}/{validateUrl}";
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
var res = await httpClient.GetStringAsync(new Uri(address));
reply = DeserializeSsoReply(res);
}
return reply.Succeeded;
}
}
I couldn't find what would be best practices for our scenario so I wonder if there are any possible pitfalls we can encounter with this approach?
When we had a similar situation what we did was for each valid client they had a certificate they could use to create JWTs for the destination (aka us) service. After validating at their origin, their credentials were repackaged and resigned using the certificate we shared with them. For a limited number of valid callers this helped us keep track of the true origin (based on the signing cert) as well as having a standardized payload for our use.
On receipt we validate that the audience is correct (aka us) and that signer is one of the configured signers.
Everyone keeps their own secrets and anyone you want to believe that you are you, you provide your public key. The owner of the key (aka you) always controls your secret but everyone has to agree on the end to end protocol.
I don't consider this reinventing so much as securing the popular path and limiting entry to a know set of users.
Related
My Project:
Web API project - ASP .NET Framework 4.8
Problem?
The code flow is as follows:
1.) The API is called -> it must call another API -> 2.) Get JWT authentication token -> 3.) Calls the desired method.
The problem is if my API is called 100 times, I will make 100 calls for the GetJwtToken() method and another 100 for the desired method itself, which seems like an overhead on the auth server. The token itself has a lifespan of 2 hours.
Are there any documented best practices on how to manage a Web API JWT token in another Web API?
What have I tried?
I've tried the following solutions and I'm still not sure whether they could be considered good practices.
One static class with two static properties Token and ValidTo and one static method GetJwtToken() that updates those properties. Before each call to the desired external API method, we check the ValidTo property and update the Token value if it has expired, via the static method.
In our service, we have one static private field Token.The method that calls the external API method is surrounded by a try catch blocks. The Catch(WebException ex) expects an Unauthorized exception if the token has expired. I check for HTTP Status Code 401 - Unauthorized.
if (response.StatusCode == HttpStatusCode.Unauthorized)
In case we go into that if clause we update the Token property by calling the GetJwtToken() method inside the catch block and then calling recursively the method again. In this way, we update the token only when it has expired and an unauthorized exception was thrown.
Another idea that I got, but didn't test isActionFilterAttribute with overridden OnActionExecuting(HttpActionContext actionContext) method. Before we go into the Web API controller the action attribute has already checked whether we have Token and if it has expired. The problem here was I am not sure where to save the Token property. Possibly as a static value in another class.
Are there any other ways to manage a JWT Token of a Web API inside another Web API and what is considered best practices?
Some code snippets, pseudo-code, or articles would be appreciated.
Edit1:
I've read this question, but it doesn't help me, since it's about how to manage the token on the front end part. The project here is Web API it's all on the server-side.
Edit2:
Edited some sentences here and there so it's more readable.
Edit3:
Added one more option that I thought about.
I'd handle this in some kind of BaseApiService
public class BaseApiService
{
private readonly IHttpClientFactory httpClientFactory;
private readonly ITokenHandler tokenHandler;
public BaseApiService(IHttpClientFactory httpClientFactory, ITokenHandler tokenHandler)
{
this.httpClientFactory = httpClientFactory;
this.tokenHandler = tokenHandler;
}
protected async Task<HttpResponseMessage> RequestAsync(HttpRequestMessage requestMessage)
{
var httpClient = httpClientFactory.CreateClient();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenHandler.Token);
var response = await httpClient.SendAsync(requestMessage);
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
var token = await tokenHandler.UpdateTokenAsync();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
return await RequestAsync(requestMessage);
}
}
return response;
}
}
Which would be responsible for making request, response serialization (notice I've used string responses for simplicity sake) and handling token for each request. Also you might want to consider handling errors and also handle infinite loops because it's currently calling self (e.g. on second call if it's unauthorized again, exit with error).
Token handler would be defined as singleton in DI and this is implementation
public interface ITokenHandler
{
string Token { get; }
Task<string> UpdateTokenAsync();
}
public class TokenHandler : ITokenHandler
{
private readonly IHttpClientFactory httpClientFactory;
public string Token { get; private set; }
public TokenHandler(IHttpClientFactory httpClientFactory)
{
this.httpClientFactory = httpClientFactory;
}
public async Task<string> UpdateTokenAsync()
{
var httpClient = httpClientFactory.CreateClient();
var result = await httpClient.PostAsync("/external-api/token", new FormUrlEncodedContent(new []
{
new KeyValuePair<string, string>("username", "external-admin"),
new KeyValuePair<string, string>("password", "external-password"),
}));
// or handle it however you want
var token = result.IsSuccessStatusCode
? await result.Content.ReadAsStringAsync()
: null;
if (!String.IsNullOrEmpty(token))
{
Token = token;
}
return Token;
}
}
And this is how you'd consume your BaseApiService
public class TodoService : BaseApiService
{
public TodoService(IHttpClientFactory httpClientFactory, ITokenHandler tokenHandler)
: base(httpClientFactory, tokenHandler)
{
}
public async Task<string> GetTodoAsync(int id)
{
var response = await RequestAsync(new HttpRequestMessage(HttpMethod.Get, $"/todo/{id}"));
return await response.Content.ReadAsStringAsync();
}
}
I don't think you need to add any ValidTo logic, but just rely on your Unauthorized response from 3rd party API, because you'll just complicate your code and you'll have to handle Unauthorized responses anyway.
Only thing is that you might lock getting/setting of token from TokenHandler, but this is just a basic example to show an idea how I'd implement it.
I will expand my comments to answer because of the characters limit.
First, re-consider / re-examine why do you need to call the auth server for every API call? Do you have a data store of some kind like a database, a cache (in memory or remote), a Azure blob storage or a shared folder? If you have, you could consider persist your access tokens to your choice of data store.
Now, let's deal with token expiration time. Depends on how the external API grants the access tokens (I assume it is OAuth2 here), you usually could access the expiration time of a token, for example using expires_in in the response. The expires_in is equal to seconds since the unix epoch, so you should know when the token will expire. You could then save the token granted to your data store along with their expiration time and refresh token. When you use cache, you could set the cache entry to expire minutes before the token in it expires.
When you get next API call, check if you have a "valid" token from your data store. If no, call to get new JWT token and persist it using above method. Otherwise, try make API call with the token from your data store. If you have a background service, like a WebJob or Hangfire, you could periodically validate all tokens against the token validation endpoint (if your external API provides one) and refresh them when needed.
You should always handle unauthorized responses. Tokens can be revoked before they expire. In the case of unauthorized response received at your end, you could try re-authenticate with the external API and refresh the token kept in your data store. If the token generation needs to get user involved, you could return 401 to your client.
Lastly, you will also need to consider security. When you persist the tokens, even to your own data store, you need to encrypt them. This is for ASP.NET Core, but still worth reading it and do something similar in your API.
Perhaps consider you API to save the AuthToken (stateful).
I'm confused though of your current flow, usually you'll have an AuthApi that has the function to provide AuthorizationTokens.
Once the caller has the AuthToken it should save it; for front-end as you know, local storage, session storage, and cookies are considered, for backend or an API you could consider a stateful API which will save the Token in a global state, so it can append it to every request without going back-and-forth with your AuthApi (It will do a trip when the token expires though, etc.).
I am having a web application with web service and client will register their application using my web application.
Now client will have application of type SPA or mobile apps and they will consume my webservices from their apps.
So I would be implementing token based mechanism for securing access to my endpoints.
1) But here I am confused that shall I use any framework to generate access token or I can use any library which will generate any random string which i will send in response.for instance something like this :
TokenId = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("+", "_")
So while registering application if client have enabled authentication for their application then user will be validated and then I will return access token and also save accesstoken in my database with that user id.
So my database table would be like below for storing validated accesstoken :
Id(autogenerated) accesstoken userid clientid createdat expiresat
So after user is authenticated and now if user want to access any protected resources then user need to pass this access token in subsequent call in header.
So what I will do I will get access token from header and then validate that accesstoken against that database and then allow access to my protected resource other wise user would get authorized.
I have seen lots of things related to this so basically this is oauth2 and I want to implement this.
I have seen Openid connect(this project doesnt even compile) which is on top of oauth2 and which is used for authentication and oauth2 will be used for authorization.
But here as I am storing access token in my database so here is my doubt related to that :
2) Now do I need openconnectid (but this project doesn't even compile) for validating access token or as I am having storing access token in my database I don't need openconnectid?
3) I want to implement asp.net identity but then I will receive dynamic database connection string and as i have seen asp.net identity mostly works with entity framework I couldn't find any source where I could use ado.net to validate username and password using SQL query. I know I can do something like this :
Make a custom user class which implements IUser as described here
Define a custom user store which implements
public class UserStoreService
: IUserStore<CustomUser>, IUserPasswordStore<CustomUser>
But I won't be having this information as I don't have fixed connection string.connection string again is stored in database with client registration.
4) We have given user a fixed endpoint through which client can create an admin so for that I will use my RSA algorithm for password hashing and then store it in database. So with this now do i need to use asp.net identity?
5) I have seen lots of following link with token based implementation but I am not getting where they are validating accesstoken in which part but now as I am having accesstoken stored in my database do I need to use any of the following implementation?
http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
6) Moreover if for any client if client don't want authentication for its respective application then what I will do is I will don't have that username password validation but I will simply generate accesstoken and then send in response so then in every subsequent request that access token will be pass to access protected resources.Do you think this make sense?
I have never seen any example where access token is store in database and problem with storing access token in database would be I have to make a call to database every time to validate access token for each endpoint.
Update :
Use case of my webservice engine would be:
1) Support multiple client application.
2) Manage user session in the form of token management for each client application. So here as most of the article is storing accesstoken in identity and that identity is validated inside [Authorize] attribute in which accesstoken is also validated and based on that user is allowed to access protected resources.This is what my understanding is up until now.
So if I also user identity and store user context inside identity supporting multiple client application is a good idea?
From “7.1. Access Token Representation” in “Full-Scratch Implementor of OAuth and OpenID Connect Talks About Findings” :
How should an access token be represented? There are two major ways.
As a meaningless random string. Information associated with an access
token is stored in a database table behind an authorization server.
As a self-contained string which is a result of encoding access token
information by base64url or something similar.
Pros and cons of these two ways are described in the blog.
If access tokens are random strings, pieces of information associated with the access tokens (user ID, client ID, scopes, lifetime, etc.) are stored in a database which is managed by the authorization server which have issued the access tokens.
Whenever a resource server which exposes APIs accepts an API call from a client application, the resource server has to get the information about the access token in some way or other.
If the resource server can access the database managed by the authorization server (in other words, if the resource server and the authorization server shares the database), the resource server can get the information about the access token from the database directly.
Otherwise, the resource server has to make an API call to the authorization server to get the information. In this case, it can be expected that the authorization server exposes an API which complies with RFC 7662 (OAuth 2.0 Token Introspection). Note that some implementations may provide a more developer-friendly API than RFC 7662 (e.g. “4. Introspection Access Token”).
Anyway, your resource server doesn't necessarily have to make a DB call (or an introspection API call to the authorization server) every time if the server caches information about access tokens in a memory cache or somewhere else appropriate.
BTW, what you need when you want to protect APIs is access tokens. Therefore, your system doesn't have to support OpenID Connect which is a specification as to how to request and issue ID tokens. You may be confused because a server which supports OpenID Connect can issue access tokens, too, in addition to ID tokens. See “Diagrams of All The OpenID Connect Flows” to understand what a server which supports OpenID Connect issues.
Finally, identity management, user authentication, and OAuth 2.0 & OpenID Connect don't necessarily have to be implemented in a monolithic way. See “New Architecture of OAuth 2.0 and OpenID Connect Implementation” for details.
No, you don't need to store the access_token on the database. You can decrypt the JWT and read the information as you are the one who encrypts it with a secret key. (By default it's the machine key.)
Identity has a off the self support for Oauth. You have to just configure it properly. You can set-up the configuration for OAuthAuthorizationServerOptions in the Startup.Auth.cs. Sample code as follows. I have tried to answer most of your question in comments in the code.
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
public void ConfigureOAuth(IAppBuilder app)
{
// Configure the application for OAuth based flow
PublicClientId = "theDragonIsAlive";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new YourOwnApplicationOAuthProvider(PublicClientId),
//AuthorizeEndpointPath = new PathString("/Access/Account"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(7)
//AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
// This where you are validating the username and password credentials.
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("Dragon Fire:", "The user name or password is incorrect. You shall be burnt.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(oAuthIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
// This method is where you will create the client access token.
// First you get the client, you can place values from the client record into the tokens claim collection.
// You then create a new ClaimsIdentity.
// You add some claims, in the example client name is added.
// Create an AuthenticationTicket using your claims identity.
// Validate the ticket (you do need to do this or the client will be considered unauthenticated)
//public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
//{
// var client = clientService.GetClient(context.ClientId);
// var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
// oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName));
// var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
// context.Validated(ticket);
// return base.GrantClientCredentials(context);
//}
// This method has to be implmented when you are maintaining a list of clients which you will allow.
// This method is for validating the input, you can used this method to verify the client id and secret are valid.
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//string clientId;
//string clientSecret;
//context.TryGetFormCredentials(out clientId, out clientSecret);
//if (clientId == "1234" && clientSecret == "12345")
//{
// context.Validated(clientId);
//}
//return base.ValidateClientAuthentication(context);
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
The sample code above does not have separate client classifications. It will treat all users as a single type of client. But I have given some example code in the comments which will guide you to get started in the right direction.
Disclaimer: I am not an expert on this(yet) and my setup is different. I had an existing MVC application with Owin and I had to build a webapi on top of it. This was my prototype code and it did the job. You will have to improve in it for your production code. Have fun and good luck.
In my asp.net MVC web-site, I have implemented SSO in which the IDP/ADFS sends the SAML response and I verify the SAML token to allow users to access the web-site. I am using customized code (using System.IdentityModel.dll and System.IdentityModel.Services.dll libraries to verify the SAML response and token, instead of having.net framework do the checking with web.config settings).
So far the code seems to be working fine, but since I am new to this domain have concern that hacker would able to bypass the validation with properly constructed SAML response. Recently I tried my hands on the SAML token generation part and I realized that my token verification code can be bypassed if the token is generated intelligently.
At high level this is what I am doing to verify the token:
Extract the security token from the request.
Verify the token is valid and untouched by checking the digest value with the provided X509 public key(which is present in the SAML response)
Extract the claims/identity of the user and allow the access.
My concern is that if hacker creates SAML token (like my own token generator code) and add public key in the response and post it to my web site, my web site will successfully validate the response as the response itself is well formed and signed. Is this a valid concern? I am missing some basic validations to handle this scenario?
I can think of following options to mitigate the risk:
Check the URLReferrer and make sure that the SAML response is posted from the expected entity. I am not sure if there is a way to manipulate the URLReferrer.
Avoid using the public key present in the response to validate the digest value. I can store the X509 certificate on my end and use it to validate the response. The hacker won’t able to sign the response with the same certificate as he won’t have the private key. If this is the right way, can someone suggest me how to instruct “tokenhandler” to ignore the public key present in the response and use explicit public key?
Is there a way by which I can make back-end call to IDP, May a web-service call, and check that the token received by my web site is indeed generated by the IDP?
Since I am a newbie, I might be missing the basic SAML validation concept. So please let me know if my concern is legitimate.
Here is the sample code I use to validate the response.
public ActionResult SAMLAssert()
{
var fam = new WSFederationAuthenticationModule();
var request = this.HttpContext.Request;
// Get the security token from the SAML response
var securityToken = fam.GetSecurityToken(request);
var config = new SecurityTokenHandlerConfiguration
{
CertificateValidator = X509CertificateValidator.None,
IssuerNameRegistry = new CustomIssuerNameRegistry(),
};
config.AudienceRestriction.AudienceMode = AudienceUriMode.Never;
var tokenHandler = new Saml2SecurityTokenHandler
{
CertificateValidator = X509CertificateValidator.None,
Configuration = config,
};
//// validate the token and get the ClaimsIdentity out of it
var identity = tokenHandler.ValidateToken(securityToken);
bool isSuccess = identity[0].IsAuthenticated;
// Code to retrieve the claims/user information from the token
//....
return View();
}
And here is the custom "IssuerNameRegistry".
public class CustomIssuerNameRegistry : IssuerNameRegistry
{
public override string GetIssuerName(SecurityToken securityToken)
{
X509SecurityToken x509Token = securityToken as X509SecurityToken;
return x509Token.Certificate.Subject;
}
}
I have suspicion that the custom class is the problematic part as it’s not doing any validation.
I don't think you should check the referrer value. It can easily be spoofed.
The IdP uses its private key to sign responses sending to you. An attacker just doesn't have access to this private key. Therefore, if an attacker wants to spoof a signature, he will need to use his own certificate and put his public key to the token. While you are right that validation code uses the embedded public key for verifying signature, AFAICT it also does one more thing: check if the public key is trusted by our machine. The trust here can be established by adding the public key to your Windows Certificate store -> TrustedPeople. I don't have all the code to verify this though but it should work this way, or it should provide you a way to do that.
If you have full control over the IdP, alternative to embedded public key (aka X509Data), you can use keyname, subject name, thumbprint only. But how much more secure they provide and how to implement are out of the scope of this question.
No, SAML protocol wasn't designed to work that way. The closest thing to this is to use Artifact flow in which the IdP only returns an Artifact to your application and it needs to make an ArtifactResolve request to the IdP to get the actual Response. See What is the purpose of a SAML Artifact?. But then you still need to verify signature of the received Response.
I just finished this excellent article about securing an ASP.NET Web API 2 application with OAuth2 against an ADFS/Windows Azure AD instance using ADAL and OWIN middleware components.
However, it seems that the whole authentication workflow described in this article is very much "hard-wired" into the HTTP request pipeline and doesn't leave any room for the implementation of authentication workflows against other authentication providers.
Why is this needed?
I have a mobile web client in which "internal" and "external" users are allowed to authenticate in order to issue requests for user relevant data against an API endpoint.
While the "internal" users are obtaining their authentication tokens from Azure AD/ADFS the "external" users have to authenticate against another system which issues another kind of authentication token.
Therefore I have to be able to distinguish between requests from "internal" and "external" users on the API endpoint level in order to kick off the correct evaluation workflow for their different authentication tokens.
Any indications on how to achieve this would be highly appreciated.
Regards, Matthias
After a little bit of digging I found the following answer which describes how to programmatically validate an JWT based authentication token issued by an ADFS OAuth 2.0 authentication flow using the JwtSecurityTokenHandler class. Code examples can be found in the linked answer.
This would allow me to create a custom authorization filter which I can then use as an attribute on controllers or controller methods. This filter would analyze the Authorization header in the client request, detect the type of authentication token contained in it and then kick off the respective program logic to validate/verify the authentication token.
Something along these lines maybe:
public enum AuthTokenType
{
OAuth2Bearer,
Custom
}
public class CustomAuthenticationAttribute : IAuthenticationFilter
{
public bool AllowMultiple
{
get
{
throw new NotImplementedException();
}
}
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage incommingRequest = context.Request;
HttpHeaders headers = incommingRequest.Headers;
string authHeader = GetHeader(headers, "Authorization");
AuthTokenType authTokenType = DetecteAuthTokenType(authHeader);
if (authTokenType == AuthTokenType.OAuth2Bearer)
{
// Validate auth token using the JwtSecurityTokenHandler class
}
else if (authTokenType == AuthTokenType.Custom)
{
// Validate auth token using whatever is necessary
}
else
{
// auth token doesn't correspond to a recognized type or hasn't been part of the client request - reject request
}
}
public AuthTokenType DetectAuthTokenType(string authHeader)
{
// Analyze the authorization header string and return its proper type
}
private string GetHeader(HttpHeaders headers, string key)
{
IEnumerable<string> keys = null;
if (!headers.TryGetValues(key, out keys))
return null;
return keys.First();
}
}
I have a WCF 4.0 REST service on server side (hosted in IIS) and an Android client. The Android client sends an encrypted security token in a custom HTTP header in order to authenticate the user. I have implemented a custom ServiceAuthorizationManager which extracts the security token from the header. The token contains the username which I can read from the token:
public class MyAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
var requestMessage = operationContext.RequestContext.RequestMessage;
var requestProperty = (HttpRequestMessageProperty)requestMessage
.Properties[HttpRequestMessageProperty.Name];
var token = requestProperty.Headers["X-MyCustomHeader"];
if (!string.IsNullOrEmpty(token))
{
var userName = GetUserNameFromToken(token);
if (!string.IsNullOrEmpty(userName))
{
// How to save userName now so that I can
// retrieve it in the service operations?
return true;
}
}
return false;
}
}
Now, my problem is that I also need the name of the authenticated user in various service operations (mainly to access user profile data) and I was planning to retrieve it this way:
public void MyServiceOperation()
{
string userName = OperationContext.Current
.ServiceSecurityContext.PrimaryIdentity.Name;
// check profile store for that userName and do something depending on
// profile settings
}
How can I set this username in CheckAccessCore?
A very naive trial like this...
operationContext.ServiceSecurityContext.PrimaryIdentity.Name = userName;
...doesn't work because PrimaryIdentity.Name is readonly. I assume that more sophisticated code is required.
After some research I didn't find a way to set the identity in ServiceAuthorizationManager.CheckAccessCore. This method seems to be called too late in the processing pipeline when the user's identity is already set (possibly to "Anonymous" (IsAuthenticated is false)) and cannot be changed anymore. The ServiceAuthorizationManager is made for authorization, not authentication, therefore it's the wrong place to implement a custom authentication.
I've finally found three possible ways for my problem:
As explained in the article linked in #TheCodeKing's answer, using the WCF REST Starter Kit provides the option to write a custom RequestInterceptor which hooks in early enough into the pipeline, allows access to the incoming request and allows to set the user's identity based on, for example, custom HTTP headers. Unfortunately the WCF REST Starter Kit is old (based on WCF 3.5) and the development has apparently been abandoned. Some of its features have been incorporated into WCF 4.0, but some have not, and the RequestInterceptor is among them. Nonetheless I've used this solution now and mixed the Microsoft.ServiceModel.Web assembly from the Starter Kit into my WCF 4.0 solution. It seems to work so far after a few simple tests.
If the identity is not really necessary but only the user name, a simple "trick"/"hack" to write the user name into a new request header works (also in CheckAccessCore):
// ...
var userName = GetUserNameFromToken(token);
if (!string.IsNullOrEmpty(userName))
{
requestProperty.Headers["X-UserName"] = userName;
return true;
}
// ...
And then in the service methods:
public void MyServiceOperation()
{
string userName = WebOperationContext.Current.IncomingRequest
.Headers["X-UserName"];
// ...
}
Another and more low level option would be to write a custom HttpModule which intercepts the incoming request and sets the identity. An example how this could look like is here from Microsoft Pattern & Practices team (see the "HTTP Module Code" example in the mid of the article).
Take a look at this article. It has examples of setting up the IAuthorizationPolicy instance. By creating your own implementation you can control the creation of the IPrincipal and IIdentity instances which are passed around in the context. It's all hooked in from a service interceptor.
internal class AuthorizationPolicyFactory
{
public virtual IAuthorizationPolicy Create(Credentials credentials)
{
var genericIdentity = new GenericIdentity(credentials.UserName);
var genericPrincipal = new GenericPrincipal(genericIdentity,
new string[] { });
return new PrincipalAuthorizationPolicy(genericPrincipal);
}
}