Now this I like :-)
Top search result - awesome.
Enjoy!
Ideas and thoughts about Microsoft Identity, C# development, cabbages and kings and random flotsam on the incoming tide
Friday, May 26, 2017
Thursday, May 25, 2017
ADFS : The case of the slanting quote
I don't know the official name of this character but it's the double quote sign that slants to the right.
There was a question on the forum about a claims rule that threw "POLICY0002: Could not parse policy data" when the user tried to save it.
It looked like:
c:[Type == "http://adatum.com/data1holder", Issuer == "AD AUTHORITY"]
=> issue(type = "http://adatum.com/data1", Value = RegExReplace(c.Value,”'”,””);
The problem was the slanting quotes in the RegExReplace.
Using the double quote key (") on the keyboard and replacing these gives:
c:[Type == "http://adatum.com/data1holder", Issuer == "AD AUTHORITY"]
=> issue(type = "http://adatum.com/data1", Value = RegExReplace(c.Value,"'","");
which parses no problem.
Enjoy!
There was a question on the forum about a claims rule that threw "POLICY0002: Could not parse policy data" when the user tried to save it.
It looked like:
c:[Type == "http://adatum.com/data1holder", Issuer == "AD AUTHORITY"]
=> issue(type = "http://adatum.com/data1", Value = RegExReplace(c.Value,”'”,””);
The problem was the slanting quotes in the RegExReplace.
Using the double quote key (") on the keyboard and replacing these gives:
c:[Type == "http://adatum.com/data1holder", Issuer == "AD AUTHORITY"]
=> issue(type = "http://adatum.com/data1", Value = RegExReplace(c.Value,"'","");
which parses no problem.
Enjoy!
Monday, May 15, 2017
ADFS : You can change anything in the Theme structure
The official document around onload.js and customising the ADFS login page for 2012 R2 and 2016 (ADFS 3.0 and 4.0) is here.
The Theme directory structure looks like:
Directory of C:\...\Theme\Custom
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 09:26 PM css
05/11/2017 09:26 PM illustration
05/11/2017 09:26 PM images
05/11/2017 09:26 PM script
0 File(s) 0 bytes
Directory of C:\...\Theme\Custom\css
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 09:26 PM 8,144 style.css
05/11/2017 09:26 PM 8,146 style.rtl.css
2 File(s) 16,290 bytes
Directory of C:\...\Theme\Custom\illustration
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 09:26 PM 116,699 illustration.png
1 File(s) 116,699 bytes
Directory of C:\...\Theme\Custom\images
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/14/2017 08:45 PM idp
0 File(s) 0 bytes
Directory of C:\...\Theme\Custom\images\idp
05/14/2017 08:45 PM .
05/14/2017 08:45 PM ..
05/11/2017 09:26 PM 931 idp.png
05/11/2017 09:32 PM 1,727 localsts.png
05/11/2017 09:26 PM 1,977 otherorganizations.png
3 File(s) 4,635 bytes
Directory of C:\...\Theme\Custom\script
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 10:23 PM 5,925 onload.js
1 File(s) 5,925 bytes
The document refers to onload.js but you can use the same commands for any of the above files.
@Pierre e.g. has blogged on changing the "thumbnails" here.
The command for this then looks like:
Set-AdfsWebTheme -TargetName MyTheme -AdditionalFileResource @{Uri="/adfs/portal/images/idp/idp.png";path="C:\MyTheme\images\idp\idp-adfs.png"}
Enjoy!
The Theme directory structure looks like:
Directory of C:\...\Theme\Custom
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 09:26 PM css
05/11/2017 09:26 PM illustration
05/11/2017 09:26 PM images
05/11/2017 09:26 PM script
0 File(s) 0 bytes
Directory of C:\...\Theme\Custom\css
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 09:26 PM 8,144 style.css
05/11/2017 09:26 PM 8,146 style.rtl.css
2 File(s) 16,290 bytes
Directory of C:\...\Theme\Custom\illustration
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 09:26 PM 116,699 illustration.png
1 File(s) 116,699 bytes
Directory of C:\...\Theme\Custom\images
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/14/2017 08:45 PM idp
0 File(s) 0 bytes
Directory of C:\...\Theme\Custom\images\idp
05/14/2017 08:45 PM .
05/14/2017 08:45 PM ..
05/11/2017 09:26 PM 931 idp.png
05/11/2017 09:32 PM 1,727 localsts.png
05/11/2017 09:26 PM 1,977 otherorganizations.png
3 File(s) 4,635 bytes
Directory of C:\...\Theme\Custom\script
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 10:23 PM 5,925 onload.js
1 File(s) 5,925 bytes
The document refers to onload.js but you can use the same commands for any of the above files.
@Pierre e.g. has blogged on changing the "thumbnails" here.
The command for this then looks like:
Set-AdfsWebTheme -TargetName MyTheme -AdditionalFileResource @{Uri="/adfs/portal/images/idp/idp.png";path="C:\MyTheme\images\idp\idp-adfs.png"}
Enjoy!
Friday, May 12, 2017
ADFS : Avoiding the Home Realm Discovery screen by using a link
I was asked if given the HRD screen is just "buttons" and there was a "link" behind that and users always picked the same one. could they not simply add the "link" to their web site and avoid the HRD screen altogether?
The basic problem with this is that the link is not static – it’s actually dynamic.
Take SAML-P as a example.
Assume the HRD screen has two entries; ADFS A and ADFS B.
The SAML request from the application to ADFS A gives:
https://adfs-a/adfs/ls/?SAMLRequest=…
And then selecting ADFS B via HRD gives:
https://adfs-b/adfs/ls/?SAMLRequest=...
The SAML request itself is different because as the packet goes through each ADFS, it’s signed with that ADFS’s signing key.
So the "link" would have to be updated every time a signing certificate changed.
In addition, a section of the SAML AuthnRequest contains:
IssueInstant="2017-05-11T01:27:25.56Z"
The request contains the time the request was sent – which is dynamic.
There is an ADFS parameter for SAML called “clock skew”.
ADFS rejects the request when the skew between the server clock and the time in the request is more than 30 seconds (by default).
So nice try but no cigar!
Enjoy!
The basic problem with this is that the link is not static – it’s actually dynamic.
Take SAML-P as a example.
Assume the HRD screen has two entries; ADFS A and ADFS B.
The SAML request from the application to ADFS A gives:
https://adfs-a/adfs/ls/?SAMLRequest=…
And then selecting ADFS B via HRD gives:
https://adfs-b/adfs/ls/?SAMLRequest=...
The SAML request itself is different because as the packet goes through each ADFS, it’s signed with that ADFS’s signing key.
So the "link" would have to be updated every time a signing certificate changed.
In addition, a section of the SAML AuthnRequest contains:
IssueInstant="2017-05-11T01:27:25.56Z"
The request contains the time the request was sent – which is dynamic.
There is an ADFS parameter for SAML called “clock skew”.
ADFS rejects the request when the skew between the server clock and the time in the request is more than 30 seconds (by default).
So nice try but no cigar!
Enjoy!
ADFS : Augmenting the default JWT with additional attributes
This is for Server 2016 - ADFS 4.0.
The standard use case is for an ASP.NET application using OpenID Connect / OAuth via the NuGet OWIN packages taking to ADFS.
The standard way is to configure a server application as above.
This all works . The problem is that the ADFS wizard does not have any way to configure claims rules. As a result you get the standard set of claims in the JWT.
aud 8173...1501
iss https://xxx.cloudapp.net/adfs
iat 1494378378
exp 1494381978
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant 1494378377
nonce 6362....NmVl
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier 0JrA...H7k=
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn user1@dev.local
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name DEV\user1
c_hash baOc...R8GA
The only user specific details are the "upn" and the "name".
But what if you want more?
You can use the "Web browser accessing a web application" profile as per this.
This profile achieves the result in a roundabout way:
"Behind the scenes, this template creates a native client and new app type called Web application, which is just a Web API with an Identifier (RPID) that matches the native client's client ID. This means the Web application is simultaneously client and resource, so you can assign issuance transform rules as you would with a Web API."
I configured some claims in the wizard:
Now when I authenticate, I get this:
aud c906...3ab4
iss https://xxx.cloudapp.net/adfs
iat 1494379409
exp 1494383009
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant 1494379408
nonce 6362...NTk2
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier PRUA...50U=
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name DEV\user1
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn user1@dev.local
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname User1
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname Test
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress user1@company.com
http://schemas.xmlsoap.org/claims/CommonName User1 Test
apptype Public
appid c906...3ab4
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
ver 1.0
http://schemas.microsoft.com/identity/claims/scope openid
c_hash -0ZX...Z81A
You will also notice that the apptype is "Public" i.e. a web site as opposed to "confidential" i.e. a web API.
I also answered a question in stackoverflow along these lines here.
As per the discussion from @Efrain:
"As indicated in @nzpcmad's answer, it appears that custom claims in the id_token using the default URL-parameter-encoded GET redirect is simply not supported. The reason for this may be that there is an URL length limit, but I find that quite questionable.
Enjoy!
The standard use case is for an ASP.NET application using OpenID Connect / OAuth via the NuGet OWIN packages taking to ADFS.
The standard way is to configure a server application as above.
This all works . The problem is that the ADFS wizard does not have any way to configure claims rules. As a result you get the standard set of claims in the JWT.
aud 8173...1501
iss https://xxx.cloudapp.net/adfs
iat 1494378378
exp 1494381978
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant 1494378377
nonce 6362....NmVl
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier 0JrA...H7k=
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn user1@dev.local
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name DEV\user1
c_hash baOc...R8GA
The only user specific details are the "upn" and the "name".
But what if you want more?
You can use the "Web browser accessing a web application" profile as per this.
This profile achieves the result in a roundabout way:
"Behind the scenes, this template creates a native client and new app type called Web application, which is just a Web API with an Identifier (RPID) that matches the native client's client ID. This means the Web application is simultaneously client and resource, so you can assign issuance transform rules as you would with a Web API."
I configured some claims in the wizard:
Now when I authenticate, I get this:
aud c906...3ab4
iss https://xxx.cloudapp.net/adfs
iat 1494379409
exp 1494383009
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant 1494379408
nonce 6362...NTk2
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier PRUA...50U=
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name DEV\user1
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn user1@dev.local
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname User1
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname Test
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress user1@company.com
http://schemas.xmlsoap.org/claims/CommonName User1 Test
apptype Public
appid c906...3ab4
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
ver 1.0
http://schemas.microsoft.com/identity/claims/scope openid
c_hash -0ZX...Z81A
You will also notice that the apptype is "Public" i.e. a web site as opposed to "confidential" i.e. a web API.
I also answered a question in stackoverflow along these lines here.
As per the discussion from @Efrain:
"As indicated in @nzpcmad's answer, it appears that custom claims in the id_token using the default URL-parameter-encoded GET redirect is simply not supported. The reason for this may be that there is an URL length limit, but I find that quite questionable.
Anyway, apparently this restriction does not apply when the token is
returned in a POST redirect. That's also why people describe it working
just fine for MVC applications.
So I was able to work around the problem by redirecting the response to a back-end API endpoint (POST), which just redirects it to the front-end (SPA) again, but as a GET request with URL-encoded parameters:
So I was able to work around the problem by redirecting the response to a back-end API endpoint (POST), which just redirects it to the front-end (SPA) again, but as a GET request with URL-encoded parameters:
public class LoginController : ApiController
{
[HttpPost]
[Route("login")]
public HttpResponseMessage Login(FormDataCollection formData)
{
var token = formData["id_token"];
var state = formData["state"];
var response = Request.CreateResponse(HttpStatusCode.Moved);
var frontendUri = ConfigurationManager.AppSettings["ad:FrontendUri"];
response.Headers.Location = new Uri($"{frontendUri}#id_token={token}
&state={state}");
return response;
}
}
Note that to change the response method from GET to POST, one simply has to add &response_mode=form_post
to the OAuth request URL."Enjoy!
Monday, May 08, 2017
ADFS : Passing NameID across CP and RP
Imagine the following:
CP A --> federated with CP B --> RP
So a user goes to the RP and via HRD on CP B selects CP A and authenticates against AD.
The claims derived from CP A need to be passed across.
On CP A we have the standard LDAP rules since the user authenticated against that AD.
One of the claims we want to pass across is NameID.
You have to configure pass-through rules on CP B and the RP.
So the claims are configured in three places.
The problem is that NameID never makes it across.
There are a number of posts from people reporting the same thing but no solution.
The way I got around it was:
Assume that we want sAMAccountName to be NameID.
On CP A, have a LDAP rule:
sAMAccountName --> http://company/claims/sAMAccountName
Plus pass-through all the other claims.
On CP B, have a Transform rule:
http://company/claims/sAMAccountName --> NameID
Plus pass-through all the other claims.
In the RP, pass-through all the claims including NameID.
Now imagine you have two RP, RP A and RP B.
RP A wants sAMAccountName to be NameID.
RP B wants UPN to be NameID.
Now we have a problem because they both share the same pipeline CP A and CP B. You can't have two different rules both passing NameID.
What you have to do on CP A is:
UPN --> http://company/claims/UPN
Here you pass both http://company/claims/sAMAccountName and http://company/claims/UPN through on CP B and then transform them at the RP level to the NameID; one for each.
Enjoy!
CP A --> federated with CP B --> RP
So a user goes to the RP and via HRD on CP B selects CP A and authenticates against AD.
The claims derived from CP A need to be passed across.
On CP A we have the standard LDAP rules since the user authenticated against that AD.
One of the claims we want to pass across is NameID.
You have to configure pass-through rules on CP B and the RP.
So the claims are configured in three places.
The problem is that NameID never makes it across.
There are a number of posts from people reporting the same thing but no solution.
The way I got around it was:
Assume that we want sAMAccountName to be NameID.
On CP A, have a LDAP rule:
sAMAccountName --> http://company/claims/sAMAccountName
Plus pass-through all the other claims.
On CP B, have a Transform rule:
http://company/claims/sAMAccountName --> NameID
Plus pass-through all the other claims.
In the RP, pass-through all the claims including NameID.
Now imagine you have two RP, RP A and RP B.
RP A wants sAMAccountName to be NameID.
RP B wants UPN to be NameID.
Now we have a problem because they both share the same pipeline CP A and CP B. You can't have two different rules both passing NameID.
What you have to do on CP A is:
UPN --> http://company/claims/UPN
Here you pass both http://company/claims/sAMAccountName and http://company/claims/UPN through on CP B and then transform them at the RP level to the NameID; one for each.
Enjoy!
Subscribe to:
Posts (Atom)