Simple Claims Transformation for an RP-STS in Geneva Framework - federated-identity

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

Related

How to integrate Google Picker with new Google Identity Services JavaScript library

Because of the known issue described in here (https://developers.google.com/identity/sign-in/web/troubleshooting) I want to update my application to be using the new gsi sign-in that uses less cookies than the previous versions and therefore might have the solution for the mentioned error...
My problem is that there's little to no documentation on how to integrate google picker with the new gsi.
I used to use gapi for some picker-related code like even loading the library gapi.load('picker', () => {}). The migration doc says to replace the apis.google.com/js/api.js with the new gsi url, and a lot of other methods such as googleAuth.signIn or gapi.client.init are now to be deprecated by 2023. But then:
How to load picker without gapi available? Or gapi still needs to be imported but will not contain any sign-in related methods?
How will I pass apiKey and scopes to be able to init googlePicker?
For methods such as GoogleAuth.isSignedIn docs simply states "Remove. A user's current sign-in status on Google is unavailable. Users must be signed-in to Google for consent and sign-in moments." what does that even mean? I need to check if user is signed in in order to not show again the popup every time they want to upload a file from gPicker...
Before, we used to have a access_token on the callback of a reloadAuthResponse or a signIn, now how do we get the token?
Sorry for the question being confusing, I'm very confused with everything. Any input helps, thanks!
I came across https://developers.google.com/identity/oauth2/web/guides/use-token-model through: How to use scoped APIs with (GSI) Google Identity Services
I changed our code to load this script: https://accounts.google.com/gsi/client, and then modified the our "authorize" function (see below) to use window.google.accounts.oauth2.initTokenClient instead of window.gapi.auth2.authorize to get the access_token.
Note that the callback has moved from the second argument of the window.gapi.auth2.authorize function to the callback property of the first argument of the window.google.accounts.oauth2.initTokenClient function.
After calling tokenClient.requestAccessToken() (see below), the callback passed to window.gapi.auth2.authorize is called with an object containing access_token.
const authorize = () =>
- new Promise(res => window.gapi.auth2.authorize({
- client_id: GOOGLE_CLIENT_ID,
- scope: GOOGLE_DRIVE_SCOPE
- }, res));
+ new Promise(res => {
+ const tokenClient = window.google.accounts.oauth2.initTokenClient({
+ client_id: GOOGLE_CLIENT_ID,
+ scope: GOOGLE_DRIVE_SCOPE,
+ callback: res,
+ });
+ tokenClient.requestAccessToken();
+ });
The way access_token is used was not changed:
new window.google.picker.PickerBuilder().setOAuthToken(access_token)
#piannone is correct, adding to their answer:
You'll still need to load 'client' code, as you're using authentication. That means you'll still include https://apis.google.com/js/api.js in your list of scripts. Only don't load 'auth2'. So, while you won't do:
gapi.load('auth2', onAuthApiLoad);
gapi.load('picker', onPickerApiLoad);
you will need to:
gapi.load('client', onAuthApiLoad);
gapi.load('picker', onPickerApiLoad);
(this is instead of directly loading https://accounts.google.com/gsi/client.js I guess.)

Claims not persisting in Session Token

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).

NetworkCredential.Domain Property

MailAddress Sender = new MailAddress("SomeOne#yahoo.com", "SomeOne", Encoding.UTF8);
SmtpClient Client = new SmtpClient("smtp.mail.yahoo.com",465);
Client.Credentials = new System.Net.NetworkCredential(Sender.Address,"Password",Domain??? );
just out of curiosity what is the third overload of NetworkCredential
I mean what we need to pass argument for Domain property . I searched on net but but no one used it(domain property) .I tried "Yahoo.com","#Yahoo.com","Yahoo" bot no every time same authentication error .It can be done without Domain property but where can we use it or what we can pass it .
The Domain property is used for NTLM authentication with Active Directory domains.
It is not used for normal login scenarios.

Bing translator HTTP API throws bad request error, how to solve this?

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

Delete Exchange 2003 Emails with WebDav

I am trying to manage an Inbox in Exchange 2003 automatically using webdav from a C# application. Looking at msdn is not helping me a whole lot as the methods described here (http://msdn.microsoft.com/en-us/library/aa142917.aspx) do not coincide at all with the samples I have found otherwise. So there are two things I am trying to determine:
Of all the fields that return from a webdav query
string reqStr =
#"<?xml version=""1.0""?>
<g:searchrequest xmlns:g=""DAV:"">
<g:sql>
SELECT
*
FROM
""http://server/Exchange/email1#domain.com/Inbox/""
WHERE ""urn:schemas:mailheader:from"" = 'email2#domain.com'
</g:sql>
</g:searchrequest>";
Which one is the unique identifier? I have browsed it (but not sure of a reference to verify the fields) and it appears at first glance that DAV:id is what I want (), but I am not wanting to work on assumptions.
Secondly, what is the correct way to programmatically delete an email after I have processed it? Would something like the following work (will it remove the entry and all related metadata). I don't want any files left orphaned on the server...
string reqStr =
#"<?xml version=""1.0""?>
<g:searchrequest xmlns:g=""DAV:"">
<g:sql>
DELETE
FROM
""http://server/Exchange/email1#domain.com/Inbox/""
WHERE ""DAV:id"" = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX'
</g:sql>
</g:searchrequest>";
And finally, what are the best online sources for investigating all the data returned in the XML from the first request, and where are all the options documented for managing the webdav interface? Looking at MSDN just hasn't been fruitful.
Look for the dav:hef tags tag in the response. They contain an url you can use to issue a delete command.
From the result of a query that gets you the msg Uri then:
var request = (HttpWebRequest)WebRequest.Create(mail.MailUri);
request.Credentials = _credential;
request.Method = "DELETE";
var response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode != HttpStatusCode.OK)
{
//something might of broke
}

Resources