Have seen Dominick Baier's videos on Pluralsight and most of this I got from there. I'm trying to do a claims transformation in .net 4.5, MVC. After a lot of messing around I can get the claims transformed, but can't get them to persist. If I just have it run my ClaimsTransformer every time no problem, but this is hitting a database so I want to cache these.
So here's what I did
class ClaimsTransformer : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return base.Authenticate(resourceName, incomingPrincipal);
}
ClaimsPrincipal transformedPrincipal = incomingPrincipal;
I then perform some database access add new claims to transformedPrincipal. Then create a new principal (probably don't need this additional instantiation but others seemed to do it), write this out:
ClaimsPrincipal newClaimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(transformedPrincipal.Claims, "ApplicationCookie"));
if (HttpContext.Current != null)
{
// this caches the transformed claims
var sessionToken = new SessionSecurityToken(newClaimsPrincipal, TimeSpan.FromHours(8));
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
}
return newClaimsPrincipal;
I can see the new claims here in newClaimsPrincipal. To force the transformation to get called I am using ClaimsTransformationHttpModule from the ThinkTecture guys and can verify that this code gets run:
context.User = transformedPrincipal;
HttpContext.Current.User = transformedPrincipal;
Thread.CurrentPrincipal = transformedPrincipal;
And my additional claims are part of the transformedPrincipal.
So looks fine - but when subsequent requests come in I don't have the additional claims. ClaimsTransformer is not called, as expected, but I only have the initial set of claims - not those added by my transformation.
After logging out, my additional claims are persisted. This is using the new Visual Studio 2013 basic MVC template with Identity 2.0etc.
What I think is happening is the login runs first:
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
and this writes the authentication cookie, before my claims are transformed. Following this my claimstransformer runs and writes its own authorization cookie so now I have two. When I logout the first one's claims are lost and not the second one's claims become active.
Am confused.com.
Thanks
Ray
Looks like you are mixing the two architectures.
ClaimsAuthenticationManager and FederatedAuthentication.SessionAuthenticationModule are the .NET 4.5 way of doing things. Also called the WIF method.
SignInManager is OWIN.
Indeed don't use the WIF things in this way when you are using OWIN.
This should clarify/solve half your problem. Now you still need a ClaimsTransform in OWIN. Some filter should do it and then persist it in the OWIN identity Cookie (haven't yet done it myself).
Related
In my globals.asax.cs, I am creating a dictionary object that I want to use in my APIs to add and get data from in the life of the application.
I do the following:
Application["MyDictionary"] = myDictionary;
In my ApiController I get a handle of it, in this way:
MyDictionary myDictionary= (MyDictionary)HttpContext.Current.Application["MyDictionary"];
So far so good, the issue is that I need to do the same in the unit test, I need to add this dictionary into my Application as well, so when I call my controller, I will be able to retrieve it, how do I do that ?
doing this:
HttpContext.Current.Application.Add("MyDictionary", myDictionary);
enter code here
doesn't work, I get an exception:
An exception of type 'System.NullReferenceException' occurred in RESTServices.Tests.dll but was not handled in user code
My HttpContext.Current is null.
Any ideas how to work around this ?
The HttpContext.Current is created when a request is received, so you need to set the Current property, before using it.
See the example below:
var myDictionary = new Dictionary<string, string>();
HttpContext.Current = new HttpContext(new HttpRequest("default.aspx", "http://localhost", string.Empty), new HttpResponse(new StringWriter(CultureInfo.InvariantCulture)));
HttpContext.Current.Application.Add("MyDictionary", myDictionary);
I have a WebApi method looking more or less like this:
public async Task<HttpResponseMessage> Get(Guid id)
{
var foo = await Store.GetFooAsync(id);
if (foo.BelongsTo != User.Identity.Name)
throw new HttpResponseException(HttpStatusCode.Forbidden);
//return foo here
}
This seems to work fine in the actual application, where a custom IHttpModule sets the principal in both HttpContext.User and Thread.CurrentPrincipal.
However, when calling from a unit test:
Thread.CurrentPrincipal = principal;
var response = controller.Get(id).Result;
User is reset after await (i.e. on the continuation), so the test fails. This happens when running under R#8, and it wasn't happening with R#7.
My workaround was to save the current principal before the first await, but it's just a hack to satisfy the test runner needs.
How can I call my controller method and make sure continuations have the same principal as the original call?
I fixed this by upgrading the MVC packages. The new version keeps the principal in ApiController.RequestContext.Principal, separate from Thread.CurrentPrincipal, avoiding the problem completely.
My new test code starts with:
controller.RequestContext.Principal = principal;
The short version is, use HttpContext.Current.Use instead of Thread.Principal.
The short version is, use Request.LogonUserIdentity or Request.RequestContext.HttpContext.User.
async/await preserves the original request context, not the thread that started the method. This makes senses, otherwise ASP.NET would have to freeze the thread and have it available for when await returns. The thread you run after await is a thread from the ThreadPool, so modifying its CurrentPrincipal is a very bad idea.
Check the transcript from Scott Hanselman's podcast "Everything .NET programmers know about Asynchronous Programming is wrong" for more.
I have inherited come code (an MVC web app) and am having trouble getting it to start.
These two lines exist:
var claimsPrincipal = principal as IClaimsPrincipal;
if (claimsPrincipal == null)
throw new ArgumentException("Cannot convert principal to IClaimsPrincipal.", "principal");
principal is an IPrincipal (in this case a System.Security.Principal.WindowsPrincipal), and is not null.
The first line sets claimsPrincipal to null, so the exception is thrown. I'm assuming it must have worked for someone at some point, and this is a fresh copy from source control. Why would this cast return null for me?
I see that this post is a long time ago. But I encountered the same problem today, and finally I get the way to resolve it.
In framework 4.5 or 4.6, you can directly cast System.Security.Principal.WindowsPrincipal into IClaimsPrincipal, because the first one implements the second one. But in framework 3.5 or 4.0, you cannot do this, becasue the first one doesn't implement the second one.It just implements IPrinciple, not IClaimsPrincipal. You can see it from MSDN link here.
Here it a way to resovle this and get the IClaimsPrincipal object.
var t = HttpContext.Current.User.Identity as WindowsIdentity;
WindowsClaimsPrincipal wcp = new WindowsClaimsPrincipal(t);
IClaimsPrincipal p = wcp as IClaimsPrincipal;
HttpContext.Current.User is a WindowsPrincipal, and finally you can get IClaimPrincipal.
principal might in fact be null. Did you debug that?
Check to see if the type of principal implements the IClaimsPrincipal interface.
Whenever I call Bing Translation API [HTTP] to translate some text, first time it works fine, and second time onwards it gives me 'bad request' [status code 400] error. If I wait for 10 or so minutes and then try again, then first request is successful, but second one onwards same story. I have a free account [2million chars translation] with Bing Translation APIs, are there any other limitations calling this API?
Thanks, Madhu
Answer:
hi, i missed to subscribing to Microsoft Translator DATA set subscription. Once i get the same, then things have solved. i.e; once i have signed up for https://datamarket.azure.com/dataset/bing/microsofttranslator then things are working.
i was generating the access_token correctly, so that is not an issue.
thanks, madhu
i missed to subscribing to Microsoft Translator DATA set subscription. Once i get the same, then things have solved. i.e; once i have signed up for https://datamarket.azure.com/dataset/bing/microsofttranslator then things are working.
i was
thanks, madhu
As a note to anyone else having problems, I figured out that the service only allows the token to be used once when using the free subscription. You have to have a paid subscription to call the Translate service more than once with each token. This limitation is, of course, undocumented.
I don't know if you can simply keep getting new tokens -- I suspect not.
And regardless of subscription, the tokens do expire every 10 minutes, so ensure you track when you receive a token and get a new one if needed, e.g. (not thread-safe):
private string _headerValue;
private DateTime _headerValueCreated = DateTime.MinValue;
public string headerValue {
get {
if(_headerValueCreated < DateTime.Now.AddMinutes(-9)) {
var admAuth = new AdmAuthentication("myclientid", "mysecret");
_headerValue = "Bearer " + admAuth.GetAccessToken();
_headerValueCreated = DateTime.Now;
}
return _headerValue;
}
}
After reading the MSDN article (http://msdn.microsoft.com/en-us/magazine/2009.01.genevests.aspx) on implementing a Custom STS using the Microsoft Geneva Framework I am a bit puzzled about one of the scenarios covered there. This scenario is shown in figure 13 of the above referenced article.
My questions are around how does the RP initiate the call to the RP-STS in order to pass on the already obtained claims from the IP-STS? How does the desired method DeleteOrder() get turned into a Claim Request for the Action claim from the RP-STS which responds with the Action claim with a value Delete which authorizes the call? I also think the figure is slightly incorrect in that the interaction between the RP-STS and the Policy Engine should have the Claims and arrows the other way around.
I can see the structure but it's not clear what is provided by Geneva/WCF and what has to be done in code inside the RP, which would seem a bit odd since we could not protect the DeleteOrder method with a PrincipalPermission demand for the Delete "permission" but would have to demand a Role first then obtain the fine-grained claim of the Delete Action after that point.
If I have missed the point (since I cannot find this case covered easily on the Web), then apologies!
Thanks in advance.
I asked the same question on the Geneva Forum at
http://social.msdn.microsoft.com/Forums/en/Geneva/thread/d10c556c-1ec5-409d-8c25-bee2933e85ea?prof=required
and got this reply:
Hi Dokie,
I wondered this same thing when I read that article. As I've pondered how such a scenario would be implemented, I've come up w/ two ideas:
The RP is actually configured to require claims from the RP-STS; the RP-STS requires a security token from the IP-STS. As a result, when the subject requests a resource of the RP, it bounces him to the RP-STS who bounces him to the IP-STS. After authenticating there, he is bounced back to the RP-STS, the identity-centric claims are transformed into those necessary to make an authorization decision and returned to the RP.
The RP is configured to have an interceptor (e.g., an AuthorizationPolicy if it's a WCF service) that grabs the call, sees the identity-centric claims, creates an RST (using the WSTrustClient), passes it to the RP-STS, that service expands the claims into new one that are returned to the RP, and the RP makes an authorization decision.
I've never implemented this, but, if I were going to, I would explore those two ideas further.
HTH!
Regards,
Travis Spencer
So I will try option 2 first and see if that works out then formulate an answer here.
I've got situation one working fine.
In my case AD FS is the Identity Service and a custom STS the Resource STS.
All webapp's use the same Resource STS, but after a user visits an other application the Identity releated claims are not addad again by the AD FS since the user is already authenticated. How can I force or request the basic claims from the AD FS again?
I've created a call to the AD FS with ActAs, now it returns my identification claims.
Remember to enable a Delegation allowed rule for the credentials used to call the AD FS.
string stsEndpoint = "https://<ADFS>/adfs/services/trust/2005/usernamemixed";
var trustChannelFactory = new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), stsEndpoint);
trustChannelFactory.Credentials.UserName.UserName = #"DELEGATE";
trustChannelFactory.Credentials.UserName.Password = #"PASSWORD";
trustChannelFactory.TrustVersion = TrustVersion.WSTrustFeb2005;
//// Prepare the RST.
//var trustChannelFactory = new WSTrustChannelFactory(tokenParameters.IssuerBinding, tokenParameters.IssuerAddress);
var trustChannel = (WSTrustChannel)trustChannelFactory.CreateChannel();
var rst = new RequestSecurityToken(RequestTypes.Issue);
rst.AppliesTo = new EndpointAddress(#"https:<RPADDRESS>");
// If you're doing delegation, set the ActAs value.
var principal = Thread.CurrentPrincipal as IClaimsPrincipal;
var bootstrapToken = principal.Identities[0].BootstrapToken;
// The bootstraptoken is the token received from the AD FS after succesfull authentication, this can be reused to call the AD FS the the users credentials
if (bootstrapToken == null)
{
throw new Exception("Bootstraptoken is empty, make sure SaveBootstrapTokens = true at the RP");
}
rst.ActAs = new SecurityTokenElement(bootstrapToken);
// Beware, this mode make's sure that there is no certficiate needed for the RP -> AD FS communication
rst.KeyType = KeyTypes.Bearer;
// Disable the need for AD FS to crypt the data to R-STS
Scope.SymmetricKeyEncryptionRequired = false;
// Here's where you can look up claims requirements dynamically.
rst.Claims.Add(new RequestClaim(ClaimTypes.Name));
rst.Claims.Add(new RequestClaim(ClaimTypes.PrimarySid));
// Get the token and attach it to the channel before making a request.
RequestSecurityTokenResponse rstr = null;
var issuedToken = trustChannel.Issue(rst, out rstr);
var claims = GetClaimsFromToken((GenericXmlSecurityToken)issuedToken);
private static ClaimCollection GetClaimsFromToken(GenericXmlSecurityToken genericToken)
{
var handlers = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers;
var token = handlers.ReadToken(new XmlTextReader(new StringReader(genericToken.TokenXml.OuterXml)));
return handlers.ValidateToken(token).First().Claims;
}