I need to validate a user against an application with custom UserName and Password. The credentials are compared with those in database and then the user can be authorized.
I configured my AppHost adding the plugin for authentication:
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[]{
new CredentialsAuthProvider()
}));
I have decorated the my DTO with [Authenticate] attribute
I then created a service to handle the Authenticate call:
public AuthenticateResponse Any(Authenticate request = null)
{
var response = new AuthenticateResponse();
// code to get user from db
//...
// check if credentials are ok
if (passInDB == request.Password)
{
var session = this.GetSession();
session.IsAuthenticated = true;
session.UserName = userFromDBEntity.Username;
response.UserId = userFromDBEntity.ID.ToString();
}
return response;
}
In the client app I created a call to the service to provides me authentication:
AuthenticateResponse authResponse = client.Post(new Authenticate
{
provider = Axo.WebServiceInterface.AxoAuthProvider.Name, //= credentials
UserName = username,
Password = password,
RememberMe = true
});
Then, still in the client, I have written something like:
if (authResponse.UserId != null)
{
client.AlwaysSendBasicAuthHeader = true;
client.SessionId = authResponse.SessionId;
}
..with the hope to get aware the client that now I am an authenticated user, but after debugging to death I'm still having an UNAUTHORIZED Exception.
I am able to reach the Authenticate Service I created, and check the credentials against the db, but after that it seems the jsonclient needs something more than "SessionId" to know that it is authenticated, because I get the error for any other request. I suppose that headers are missing something.
I read a lot of posts, and I tried also to define my custom AuthProvider and then override TryAuthenticate to see if may be helpful (for someone it was) but the method doesn't even get fired..
There's an example of using ServiceStack's Authentication to implement a Custom Auth Provider by inheriting CredentialsAuthProvider and overriding TryAuthenticate() to determine whether the userName/password is valid and OnAuthenticated() to populate the Users IAuthSession with info from the existing DB:
public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService,
string userName, string password)
{
//Add here your custom auth logic (database calls etc)
//Return true if credentials are valid, otherwise false
}
public override IHttpResult OnAuthenticated(IServiceBase authService,
IAuthSession session, IAuthTokens tokens,
Dictionary<string, string> authInfo)
{
//Fill IAuthSession with data you want to retrieve in the app eg:
session.FirstName = "some_firstname_from_db";
//...
//Call base method to Save Session and fire Auth/Session callbacks:
return base.OnAuthenticated(authService, session, tokens, authInfo);
//Alternatively avoid built-in behavior and explicitly save session with
//authService.SaveSession(session, SessionExpiry);
//return null;
}
}
Then to get ServiceStack to use your AuthProvider you need to register it with the AuthFeature plugin, e.g:
//Register all Authentication methods you want enabled for this web app
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CustomCredentialsAuthProvider(),
}
));
If everything's configured correctly you'll then be able to Authenticate with any of the Service Clients, e.g:
var authResponse = client.Post(new Authenticate
{
provider = "credentials",
UserName = username,
Password = password,
RememberMe = true
});
If successful this will return a populated authResponse, the ss-id/ss-pid Session cookies will also be populated on the client instance which will then let you call AuthOnly Services that are protected with [Authenticate] attribute.
Don't implement Authenticate Service
You never want to implement your own Any(Authenticate request) which ServiceStack already implements. The way to plug into ServiceStack's Authentication is to use a custom provider shown above. You can instead choose to ignore ServiceStack's Authentication in which case you should implement your own Custom Authentication Service but you should not use the existing Authenticate DTO's or [Authenticate] attribute which are apart of ServiceStack's Authentication support and assume that you're calling a registered AuthProvider.
Request DTO's are never nullable
Although unrelated, you also never want to make your Request DTO's nullable, e.g. Any(Authenticate request = null). ServiceStack will always call your Services with a populated Request DTO, or an empty one if no parameters were passed, it will never call your Service without a Request DTO or with a null Request DTO.
Related
I have two ServiceStack servers X and Y. Server X has functionality to register and authenticate users. It has RegistrationFeature,CredentialsAuthProvider, MemoryCacheClient and MongoDbAuthRepository features to handle the authentication.
Recently, I introduced server Y and GUI forms that talk to server Y to handle another part of my business domain. Server Y needs to make requests to authenticated endpoints on server X.
How do I configure server Y in such a way that when it gets login requests from the GUI forms, it passes that responsibility to Server X which has access to the user information?
I tried implementing a custom CredentialsAuthProvider in server Y like so:
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
// authenticate through server X
try
{
var client = new JsonServiceClient("http://localhost:8088");
var createRequest = new Authenticate
{
UserName = userName,
Password = password,
provider = Name,
};
var authResponse = client.Post(createRequest);
return true;
}
catch (WebServiceException ex)
{
// "Unauthorized
return false;
}
}
but later when I try to make a request from a service in server Y to an authenticated endpoint in server X, I get Unauthorized error.
public class MyServices2 : Service
{
public object Any(TwoPhase request)
{
try
{
// make a request to server X on an authenticated endpoint
var client = new JsonServiceClient("http://localhost:8088");
var helloRequest = new Hello
{
Name = "user of server Y"
};
var response = client.Post(helloRequest);
return new TwoPhaseResponse { Result = $"Server X says: {response.Result}" };
}
catch (WebServiceException e)
{
Console.WriteLine(e);
throw;
}
}
...
}
This is highly dependent on the method of Authentication you choose. If you want to use CredentialsAuthProvider than you must ensure each Server is configured to use the same distributed Caching Provider instance (i.e. any Caching Provider other than MemoryCacheClient). This is because when you're authenticated, the Session Cookie Ids which point to an Authenticated User Session are populated on the Service Client which is sent with each Request. The ServiceStack Instance that receives the Session Cookie Ids would use it to access the Authenticated User Session in the registered caching provider.
If both ServiceStack Services are configured to use the same Caching Provider you could transfer the Session Cookie from the incoming Request to a new Service Client with something like:
Transferring Session Id
public object Any(ClientRequest request)
{
// make a request to server X on an authenticated endpoint
var session = base.SessionAs<AuthUserSession>();
var client = new JsonServiceClient("http://localhost:8088");
client.SetSessionId(session.Id);
var response = client.Post(new Hello {
Name = "user of server Y"
});
return new TwoPhaseResponse { Result = $"Server X says: {response.Result}" };
}
Transferring BasicAuthProvider Credentials
Otherwise if you're using HTTP Basic Auth with the BasicAuthProvider then the UserName/Password is sent with the Request which you can transfer to your internal Service Client with:
var basicAuth = base.Request.GetBasicAuthUserAndPassword();
client.UserName = basicAuth.Value.Key;
client.Password = basicAuth.Value.Value;
client.AlwaysSendBasicAuthHeader = true;
Which will copy the UserName/Password sent on the incoming request and send it with the outgoing Request. But for this to work both ServiceStack Instances must be configured to use the same BasicAuthProvider and User Auth Repository since the downstream Server needs to be able to validate the UserName/Password provided.
Transferring API Key
Likewise you can use the API Key AuthProvider to do something similar but instead of forwarding UserName/Password you can forward an API Key with:
var apikey = base.Request.GetApiKey();
client.BearerToken = apikey.Id;
Again this will need to be configured with the Same ApiKeyAuthProvider and User Auth Repository as the downstream server will require validating the API Key provided.
Using JWT AuthProvider for Stateless Authentication
Otherwise if you don't want each Server to share the same infrastructure dependencies (e.g. Caching Provider / User Auth Repository) I'd look at consider using the JWT Auth Provider which is ideal for this scenarios where Authenticating with one ServiceStack Instance that issues the the JWT Token encapsulates the Users Session and lets you make authenticated Requests to other ServiceStack instances which just need to have a JwtAuthProviderReader registered.
To transfer the JWT Token you can access it with:
var bearerToken = base.Request.GetBearerToken()
?? base.Request.GetCookieValue(Keywords.TokenCookie);
and populate it on the internal Service Client with:
client.BearerToken = bearerToken;
I have a WebApi that I want to authorize my user with his linkedin information (as in create an access token and inject it in to my owin).
So far I have tried to work with Sparkle.Linkedin and this is what I have
public LinkedInLogic() {
// create a configuration object
_config = new LinkedInApiConfiguration(ApiKey, ApiSecret);
// get the APIs client
_api = new LinkedInApi(_config);
}
public Uri GetAuthUrl() {
var scope = AuthorizationScope.ReadBasicProfile;
var state = Guid.NewGuid().ToString();
var redirectUrl = "http://localhost:1510/api/login/RedirectAuth";
return _api.OAuth2.GetAuthorizationUrl(scope, state, redirectUrl);
}
public void GetAccessToken(string code) {
//If I do api.GetAccessToken(code); here I get an access token
var request = System.Net.WebRequest.Create("http://localhost:1510/api/token?grant_type=authorization_code&code=" + code);
request.GetResponse(); // my owin authorization
}
So I first get the Authorization Url -> it opens a popup -> I enter my data and it goes back to a controller which fires up GetAccessToken.
Problem is even if I completely authorize with linkedin I am not sure how to authorize with my own webapi. So I tried to send an http request to my owin token giver but it doesn't like it. There is also doesn't seem to be anyway I can return the access token back to the user so he can use it in his session.
Any ideas?
Not too sure if the sparkle is working anymore since the changes that where made by Linkedin on May 2015
Over the last few days I've been playing with the micro service pattern and all is going well but security seems to baffle me.
So If I may ask a question:
How do I handle user authentication on an individual service? At the moment I pass a request to the Gateway API which in turns connects to the service.
Question Edited Please See Below
Bearing in mind that the individual services should not know about each other. The Gateway is the aggregator as such.
Current architecture.
A little code to simulate the request:
Frontend - Client App
public class EntityRepository<T>
{
private IGateway _gateway = null;
public EntityRepository(IGateway gateway)
{
this._gateway = gateway;
}
public IEnumerable<T> FindAll()
{
return this._gateway.Get(typeof(T)).Content.ReadAsAsync<IEnumerable<T>>().Result;
}
public T FindById(int id)
{
return this._gateway.Get(typeof(T)).Content.ReadAsAsync<T>().Result;
}
public void Add(T obj)
{
this._gateway.Post(typeof(T), obj);
}
public void Update(T obj)
{
this._gateway.Post(typeof(T), obj);
}
public void Save(T obj)
{
this._gateway.Post(typeof(T), obj);
}
}
//Logic lives elsewhere
public HttpResponseMessage Get(Type type)
{
return Connect().GetAsync(Path(type)).Result;
}
public HttpResponseMessage Post(Type type, dynamic obj)
{
return Connect().PostAsync(Path(type), obj);
}
private string Path(Type type)
{
var className = type.Name;
return "api/service/" + Application.Key + "/" + className;
}
private HttpClient Connect()
{
var client = new HttpClient();
client.BaseAddress = new Uri("X");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
I use generics to determine where it needs to fire once it hit's the gateway.
So if the Type is Category it will fire the Category service thus calling:
public IEnumerable<dynamic> FindAll(string appKey, string cls)
{
var response = ConnectTo.Service(appKey, cls);
return (appKey == Application.Key) ? (response.IsSuccessStatusCode) ? response.Content.ReadAsAsync<IEnumerable<dynamic>>().Result : null : null;
}
The Gateway does not contain the physical files/Class's of the types.
After a little code, I was hoping someone could give me a little demonstration or the best approach to handle security/user authentication with the current architecture.
Case Scenario 1
User hits the web app and logs in, at that point the users encrypted email and password is sent to the Gateway API which is then passed to the User Service and decides whether the user is authenticated - all well and good but now I want to fetch all Messages from the Message Service that the user has received. I cannot really say in the Gateway if the user is authenticated, fetch the messages because that does not solve the issue of calling the Message Service outside of the Gateway API
I also cannot add authentication to each individual service because that would require all respective services talking to the User Service and that defeats the purpose of the pattern.
Fixes:
Only allow the Gateway to call the Services. Requests to services outside of the Gateway should be blocked.
I know security is a broad topic but within the current context, I'm hoping someone could direct me with the best course of action to resolve the issue.
Currently I have Hardcoded a Guid in all off the applications, which in turn fetches data if the app is equal.
Edit
This answer is about the Gateway <-> Micro service communication. The user should of course be properly authenticated when the App talks with the gateway
end edit
First of all, the micro services should not be reachable from internet. They should only be accessible from the gateway (which can be clustered).
Second, you do need to be able to identify the current user. You can do it by passing the UserId as a HTTP header. Create a WebApi filter which takes that header and creates a custom IPrincipal from it.
Finally you need some way to make sure that the request comes from the gateway or another micro service. An easy way to do that is to use HMAC authentication on a token.
Store the key in the web.config for each service and the gateway. Then just send a token with each request (which you can authenticate using a WebApi authentication filter)
To generate a hash, use the HMACSHA256 class in .NET:
private static string CreateToken(string message, string secret)
{
secret = secret ?? "";
var keyByte = Encoding.ASCII.GetBytes(secret);
var messageBytes = Encoding.ASCII.GetBytes(message);
using (var hasher = new HMACSHA256(keyByte))
{
var hashmessage = hasher.ComputeHash(messageBytes);
return Convert.ToBase64String(hashmessage);
}
}
So in your MicroServiceClient you would do something like this:
var hash = CreateToken(userId.ToString(), mySharedSecret);
var myHttpRequest = HttpRequest.Create("yourUrl");
myHttpRequest.AddHeader("UserId", userId);
myHttpRequest.AddHeader("UserIdToken", hash);
//send request..
And in the micro service you create a filter like:
public class TokenAuthenticationFilterAttribute : Attribute, IAuthenticationFilter
{
protected string SharedSecret
{
get { return ConfigurationManager.AppSettings["SharedSecret"]; }
}
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
await Task.Run(() =>
{
var userId = context.Request.Headers.GetValues("UserId").FirstOrDefault();
if (userId == null)
{
context.ErrorResult = new StatusCodeResult(HttpStatusCode.Forbidden, context.Request);
return;
}
var userIdToken = context.Request.Headers.GetValues("UserIdToken").FirstOrDefault();
if (userIdToken == null)
{
context.ErrorResult = new StatusCodeResult(HttpStatusCode.Forbidden, context.Request);
return;
}
var token = CreateToken(userId, SharedSecret);
if (token != userIdToken)
{
context.ErrorResult = new StatusCodeResult(HttpStatusCode.Forbidden, context.Request);
return;
}
var principal = new GenericPrincipal(new GenericIdentity(userId, "CustomIdentification"),
new[] {"ServiceRole"});
context.Principal = principal;
});
}
public async Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
}
public bool AllowMultiple
{
get { return false; }
}
private static string CreateToken(string message, string secret)
{
secret = secret ?? "";
var keyByte = Encoding.ASCII.GetBytes(secret);
var messageBytes = Encoding.ASCII.GetBytes(message);
using (var hasher = new HMACSHA256(keyByte))
{
var hashmessage = hasher.ComputeHash(messageBytes);
return Convert.ToBase64String(hashmessage);
}
}
}
Option 1 (Preferred)
The easy way is the micro services should be behind the gateway, hence you would whitelist services to connect to them, meaning only authorized and trusted parties have access (i.e. the gateway only). Clients shouldn't have direct access to them. The Gateway is your night club bouncer.
Option 2
You can use a JWT or some form of token and share the secret key between the services. I use JWT Authorization Bearer tokens.
The other services don't need to query the user service, they just need to know that the token is valid, then they have authorization to use the API. I get the JWT passed from the client to the gateway and inject it into the request that is sent to the other service behind, just a straight pass through.
The micro service behind needs to have the same JWT consumption as the gateway for authorization but as I mentioned that is just determining a valid token, not querying a valid user.
But this has an issue that once someone is authorized they can jump call upon other users data unless you include something like a claim in the token.
My Thoughts
The part that I found a challenge from Monolithic to Micro Services was that you needed to switch where you place your trust. In Monolithic you control everything you are in charge. The point of Micro Services is that other services are in complete control of their domain. You have to place your trust in that other service to fulfill its obligations and not want to recheck and reauthorize everything at every level beyond what is necessary.
I am working on a cross platform web app using angular and webapi. The problem is when the angular app runs in a cordova container. To play nice with the rest of the applications on the device, I am required to use a plugin for SSO.. This plugin is what is causing me issues, because it does a few things. It intercepts all the http requests and adds a bearer token to the header, which is generated by a 3rd party Token provider, so I can't decode it, and overwrites any bearer token I have set in the header.It also seems to block cookies..
So it makes it a bit tricky when you can't send you own local credentials.
So I started with https://coding.abel.nu/2014/06/writing-an-owin-authentication-middleware/ and http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthBearerAuthenticationHandler.cs
So I figured I should write my own middleware to take care of this; I thought since the standard oauth middleware can work without cookies, I should not have too hard a time getting my slightly different bearer token middleware to do it.. But that has not been the case... Writing my own middleware.. so I'm able to get the header, validate with the external token provider, but I can't actually sign in.
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
try
{
// Find token in default location
string requestToken = null;
string authorization = Request.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorization))
{
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
requestToken = authorization.Substring("Bearer ".Length).Trim();
}
}
.... Take the Request token call other Server, verify token...
Also
public override async Task<bool> InvokeAsync()
{
var ticket = await this.AuthenticateAsync();
if(ticket != null)
{
this.Context.Authentication.SignIn(new AuthenticationProperties(), grantIdentity);
return false;
}
}
So in the end the SignIn does not cause a error or anything, but does not actually signin. As soon as I get to a controller action with an [Authorize] attribute, I get a 401. I not have any external cookies enabled. There is a high probability that I am on the wrong track or I am making it way too hard.
You are doing it way too hard.
Instead of creating your own bearer authentication middleware you should change the default OAuthBearerAuthenticationProvider.
Here is a sample for sending the token in the query string.
//in Startup class
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new QueryStringOAuthBearerProvider(),
//your settings
});
//implementation
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
private const string AccessTokenQueryKey = "access_token";
public override Task RequestToken(OAuthRequestTokenContext context)
{
//check if token found in the default location - "Authorization: Bearer <token>" header
if (string.IsNullOrEmpty(context.Token))
{
var token = context.Request.Query.Get(AccessTokenQueryKey);
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
}
return Task.FromResult<object>(null);
}
}
So… I ment to answer it earlier, but I was able to figure it out, without override the authorize attribute. I ended up looking at the source for the OWIN security code. The trick is, you really need 2 OWIN middleware components. One is the what I call (and I stole this from the owin source) the server middleware. The server middleware responds to the challenge and/or if you are feeling crazy generate local credentials for you. This middleware is also a PASSIVE middleware component. I won’t get in to generating the local credentials unless someone asks , because it’s a bit off point, but if someone thinks it will be helpful, I can update.
public class LowCalorieAuthenticationServerHandler : AuthenticationHandler<LowCalorieAuthenticationServerOptions>
{
//Important this needs to be overriden, but just calls the base.
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
return Task.FromResult<AuthenticationTicket>(null);
}
/// <summary>The apply response challenge async.</summary>
/// <returns>The <see cref="Task"/>.</returns>
protected override async Task ApplyResponseChallengeAsync()
{
if (this.Response.StatusCode != 401)
{
Task.FromResult<object>(null);
return;
}
var challenge = this.Helper.LookupChallenge(
this.Options.AuthenticationType,
this.Options.AuthenticationMode);
if (challenge != null)
{
//OK in here you call the rediret to the 3rd party
//return a redirect to some endpoint
}
Task.FromResult<object>(null);
return;
}
}
Anyway notice how the override AuthenticateCoreAsync() just returns
return Task.FromResult(null);
This is because we don’t want this middleware to modify the request. ApplyResponseChallengeAsync will wait for a Challenge and redirect you to the 3rd party login. IF you want to create a local token of some sort you would override the InvokeAsync method
The second middle ware you need is the token/external credentials validator. This will then authenticate the user somehow. In the case of the local bearer token that is built into the OWIN security, it simple deserializes the token and if it can, and the token is not expired it authenticates the user. So in the case that you want to verify the token with a 3rd part sso, such as google or anything, you insert you logic here. In my case I not only wanted to call the 3rd party provider to get the user info, but to check if they token was still valid for single sign out, and to prevent multiple sessions.
public class LowCalorieAuthenticationHandler : AuthenticationHandler<LowCalorieAuthenticationOptions>
{
//Going to give you the user for the request.. You Need to do 3 things here
//1. Get the user claim from teh request somehow, either froma header, request string, or cookie what ever you want
//2. validate the user with whatever user store or 3rd party SSO you want
//3. Generate a AuthenticationTicket to send to on to the request, you can use that to see if the user is valid in any Identity collection you want.
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
//Good to throw in a point of override here.. but to keep it simple-ish
string requestToken = null;
string authorization = Request.Headers.Get("Authorization");
//TOTAL FAKEOUT.. I am going to add a bearer token just so the simple sample works, but your client would have to provide this
authorization = "Bearer 1234567869";
//STEP 1
if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
requestToken = authorization.Substring("Bearer ".Length).Trim();
return await FakeExternalBearer(requestToken);
}
return null;
}
private async Task<AuthenticationTicket> FakeExternalBearer(string token)
{
var authenticationType = Options.AuthenticationType;
//pretend to call extenal Resource server to get user //STEP 2
//CallExternal(token)
//Create the AuthTicket from the return.. I will fake it out
var identity = new ClaimsIdentity(
authenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier,"user1", null, authenticationType));
identity.AddClaim(new Claim(ClaimTypes.Name, "Jon",null, authenticationType));
var properties = new AuthenticationProperties();
properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(1);
properties.IssuedUtc = DateTime.UtcNow;
var ticket = new AuthenticationTicket(identity, properties);
return ticket;
}
}
Ok here we override AuthenticateCoreAsync, but we actually do something now. This this were your do you user authentication. This is the ACTIVE part of the middleware. Note it needs to return a valid AuthenticationTicket. This will run on each request so be careful what you call and how often.
So I have a very simple example here https://github.com/jzoss/LowCalorieOwin If anyone is interested in more detail, please ask. I can add more. I did make it too hard, because now that I understand it, it’s pretty easy, but there is really no good examples on how to do this.
I have several WCF services hosted in IIS6 (should not affect this issue) on the same host, and I want, for Performance/ Maintanance and other reasons to combine several requests into 1 request using a Facade Service,
All done with special Service Contract / Service that has an operation that calls other services for several operations.
I'm using WSHTTP (probably BasicHttp in the near future) with Message security and UserName client credential type.
I want the Facade Service to use the credentials from the client. Meaning the call to the back-end service will get the credentials as if the client would call it directly.
For example:
Client calls FacadeService.CompositeOperation with UserName "A" and password "B".
Now the FacadeService.CompositeOperation needs to call BackEndService.BackendOperation setting the Credentials.UserName.UserName to "A" and Credentials.UserName.Password to "B" just like what the client done when calling to this operation. I have no way to extract this information in WCF (and it should be, because it is sensitive information) but i neither found a way to take "a token" of these and pass it forward to the backend service (I have no need to know this information in the FacadeService, only to pass them over).
In FacadeService, as in BackEndService, the authentication is made through ASP.NET provider, the authorization is a custom Role-based authorization taking the UserName from the PrimaryIdentity, so the PrimaryIdentity on the BackEndService should be set to what the client send.
How should i do it?
I read your post yesterday but wasn't sure of an answer, but seeing as you've had no replies i thought i'd add something and maybe provide some food for thought.
Firstly, would making the additonal service calls be overly intensive on resources? If not, there is an argument for code clarity, to seperate them out so in the future developers will know exactly what's happening rather than 1 service call performing multiple operations.
Are you not able to make calls to other services from your server side code from within the method you're hitting? As once, you're server side, the security context should hold the identity of the user that you're after so calls to other services would use the same identity.
Finally, I was wondering whether WCF Impersonation (MSDN LINK) might be something you can use on the server to achieve what you're after. I've not used it myself so can't advise as much as i'd like.
Hope that's of some help - good luck!
Once i tried to Store Password along with UserName in PrimaryIdentity.
To achieve this What we need to do is to provide a New UserNameSecurityTokenAuthenticator Which will authenticate UserName and Password and then can store in the Identity and then it will Store the Identity in SecurityContext of WCF.
Steps to Do
Classes
1.) TestServiceHost : ServiceHost
2.) UserNamePasswordSecurityTokenManager : ServiceCredentialsSecurityTokenManager
3.) TestUserNameSecurityTokenAuthenticator : UserNameSecurityTokenAuthenticator
4.) MyIdentity : IIdentity
5.) MyAuthorizatoinPolicy : IAuthorizationPolicy
1.) Create New ServiceHost class TestServiceHost
2.) In TestServiceHost Override OnOpening and provide a new Class UserNamePasswordServiceCredentials
protected override void OnOpening()
{
base.OnOpening();
this.Description.Behaviors.Add(new UserNamePasswordServiceCredentials());
}
3.) Then in UserNamePasswordServiceCredentials, provide new UserNamePasswordSecurityTokenManager
public override SecurityTokenManager CreateSecurityTokenManager()
{
return new UserNamePasswordSecurityTokenManager(this);
}
4.) Then in UserNamePasswordSecurityTokenManager provide new TestUserNameSecurityTokenAuthenticator
public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
{
if (tokenRequirement.TokenType == SecurityTokenTypes.UserName)
{
outOfBandTokenResolver = null;
return new TestUserNameSecurityTokenAuthenticator();
}
return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
}
5.) Then Inside TestUserNameSecurityTokenAuthenticator you can Authenticate UseraName and Password and can create your own Identity. In this function you will return a list of IAuthorization policies to be evaluated. I created my own authorization Policy and passed my new identity to it, so as to set the Identity in context.
protected override System.Collections.ObjectModel.ReadOnlyCollection<System.IdentityModel.Policy.IAuthorizationPolicy> ValidateUserNamePasswordCore(string userName, string password)
{
ClaimSet claimSet = new DefaultClaimSet(ClaimSet.System, new Claim(ClaimTypes.Name, userName, Rights.PossessProperty));
List<IIdentity> identities = new List<IIdentity>(1);
identities.Add(new MyIdentity(userName,password));
List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
policies.Add(new MyAuthorizationPolicy(ClaimSet.System, identities));
return policies.AsReadOnly();
}
public class MyAuthorizationPolicy : IAuthorizationPolicy
{
String id = Guid.NewGuid().ToString();
ClaimSet issuer;
private IList<IIdentity> identities;
#region IAuthorizationPolicy Members
public MyAuthorizationPolicy(ClaimSet issuer, IList<IIdentity> identities)
{
if (issuer == null)
throw new ArgumentNullException("issuer");
this.issuer = issuer;
this.identities = identities;
}
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
if (this.identities != null)
{
object value;
IList<IIdentity> contextIdentities;
if (!evaluationContext.Properties.TryGetValue("Identities", out value))
{
contextIdentities = new List<IIdentity>(this.identities.Count);
evaluationContext.Properties.Add("Identities", contextIdentities);
}
else
{
contextIdentities = value as IList<IIdentity>;
}
foreach (IIdentity identity in this.identities)
{
contextIdentities.Add(identity);
}
}
return true;
}
public ClaimSet Issuer
{
get { return this.issuer; }
}
#endregion
#region IAuthorizationComponent Members
public string Id
{
get { return this.id; }
}
#endregion
}
So this example shows how you can override Security in WCF:
Now in your problem:
1.) Implement this Technique and Set UserName and Password in your identity. Now when ever you have call child service, get Identity extract Username and password from it and pass on to child service.
2.) Authenticate UserName and Password and generate a token for that (should create a new token service for that). Save this Token in your Identity along with Username and pass these two to your child services. Now for this approach to work, child service has to validate your new generated token, for which you should have a token Service which can create token by validating username and password and also which can validate token along with username.
Personally I would go for approach 2, but it will introduce new overheads.