Friday, January 29, 2016

ADFS : SAML IDP Initiated SLO

ADFS v3.0 - Server 2012 R2.

Been doing a PoC with client IDP Initiated via ADFS to a SAML ASP.NET client built on the ComponentSpace SAML stack.

Getting the login to work was somewhat trivial, getting the logout to work was somewhat harder!

The first ADFS error I got was:

MSIS0040: Received LogoutRequest element that is not NameID

Looking at the actual request, I noticed NameID was missing. This was because it wasn't one of the assertions in the login.

So I added a Transform in the RP claims rules to transform email to NameID.

Still got the error and I noticed that the outgoing NameID format was different i.e. email vs. unspecified

Fixed that in the claims rule.

I then got:


The verification of the SAML message signature failed.
Message issuer: https://roryb-lt001/MvcExampleServiceProvider
Exception details:
MSIS7074: SAML authentication request for the WebSSO profile must specify an issuer with no NameQualifier, SPNameQualifier or SPProvidedId properties. 


I have no idea what the bottom part of the error means but the problem was that ComponentSpace wasn't signing the logout.

So I added:

SingleLogoutServiceUrl="https://xxxl/adfs/ls/"
SignLogoutRequest="true"

to the saml.config.

(Note that you need a logout service URL as well).

So you need:

  • A NameID
  • The NameID format in the Logout must match that in the Login
  • The Logout must be signed

The signing could also be altered by using one of the ADFS PowerShell cmdlets.

The working logout looks like:

<samlp:LogoutRequest ID="_48141811-d3ab-4d1c-b073-9d8b240489ec"
                     Version="2.0"
                     IssueInstant="2016-01-28T21:30:26.736Z"
                     Destination="https://xxx/CompanyApp/Logout.aspx"
                     Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified"
                     xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                     >
    <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://xxx/adfs/services/trust</Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
            <ds:Reference URI="#_48141811-d3ab-4d1c-b073-9d8b240489ec">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <ds:DigestValue>9Zq1Y7AlKItUJdZYnIgpMxvzq48=</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>pK+lQDO2/+zbXOSp8vzy...g82kSQ==</ds:SignatureValue>
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <ds:X509Data>
                <ds:X509Certificate>MIIC8jC...2hdhC5uDwoc=</ds:X509Certificate>
            </ds:X509Data>
        </KeyInfo>
    </ds:Signature>
    <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
            xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
            >joeb@company.com</NameID>
    <samlp:SessionIndex>_d444eed6-1063-401a-8df1-11ea3f94b556</samlp:SessionIndex>
</samlp:LogoutRequest>

Enjoy!

Wednesday, January 27, 2016

AAD : Azure AD and IDPInitiated

Came across this very interesting post in SO.

Custom SSO With Azure Active Directory.

Note the IDPInitiated section:

"Finally, if your application expects IdP initiated SSO, construct a canned SAML AuthNRequest and save it in a URL - when your organization's users will click on this URL (canned SAML AuthNRequest) - they will get redirected to Azure AD where they will sign-in and then the token will be posted to the application's AssertionConsumerServiceURL - resulting in the user getting signed in. 

You can use the following tool to create a SAML AuthNRequest":

https://www.authnauthz.com/SAML/OutboundAuthNRequest

Azure AD doesn't have an IDP Initiated endpoint so this is a neat little trick!

Enjoy!

WIF : IDPInitiated and a WS-Fed application

I discussed IDPInitiated here:

ADFS : SAML redirect to application via relayState and loginToRp 

I've been looking at a case where IDPInitiated is used to a WS-Fed application.

The use case is:

IDP --> SAML --> ADFS --> WS-Fed --> Application

IDPInitiated is a SAML feature not supported in WS-Fed but it works in ADFS if you use the RPID construct.

If you set the RPID to the identifier of an RP in ADFS, the user will use IDPInitiated on their IDP to authenticate and this will go to ADFS, ADFS will see they are authenticated and pass the token onto the RP seamlessly.

However, you will get:

ID4216: The ClaimType '' must be of format 'namespace'/'name'.

The reason is discussed here:

AD FS 2.0: The Admin Event Log Shows Error 111 with System.ArgumentException: ID4216

Essentially, WS-Fed uses a SAML 1.1 token  and SAML 2.0 uses a SAML 2.0 token and SAML 1.1 is far stricter on the claim type.

It requires:

<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress">
    <AttributeValue> tom@abc.com </AttributeValue>
</Attribute>

Instead of:

<saml:Attribute Name="EmailAddress"
    NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <saml:AttributeValue      xsi:type="xs:string">tom@abc.com</saml:AttributeValue>
</saml:Attribute>

Note the difference in the attribute name formats.

Apart from this WIF breaks because all the classes expect the stricter URI form.

To sort this out:

On the IDP side, just pass everything through:

c:[]
 => issue(claim = c);


On the RP side, you needs to do a pass-through but using a custom rule e.g.

c:[Type == "EmailAddress"]
 => issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", Value = c.Value);


That will keep WIF happy :-)

Enjoy!

Thursday, January 21, 2016

OAuth : Microsoft OWIN (for Identity) and ADAL are not general purpose libraries

I'm seeing a lot of confusion about this.

Microsoft has OWIN client libraries for OpenID Connect / OAuth2.

It also has ADAL - Active Directory Authentication Library (used mainly for native devices).

Both implement the protocols so you would think that you can use these to hook up to any server that implements OpenID Connect / OAuth.

Except that you would be wrong.

OAuth is notorious for incompatibility so you can't really mix and match.

These libraries are aimed at a specific use case i.e. the server is either Azure AD or ADFS.

In addition, the profile is hybrid flow so if your server doesn't implement that you are already out of luck,

Which is not to say that it won't work in some cases.

e.g. IdentityServer 3 does implement the hybrid flow.

Refer : IdentityServer : The WebApp-OpenIDConnect-DotNet Azure AD sample.

Here I show how to use the OWIN library to connect to IdentityServer 3. As you can see, I had to make some alterations.

Enjoy!

Monday, January 11, 2016

ADFS : SAML redirect to application via relayState and loginToRp

This post follows on from:

IDP Initiated Sign-on to SAML SP using SAML IDP

Prior reading:


The question is around having a SAML IDP (Salesforce), ADFS as the RP-STS and multiple SAML RP.

Users may authenticate with either AD (via ADFS) or via Salesforce.

The easy way is to use RP Initiated but that wasn't an option.

In the link above, there is a useful tool to generate the relayState.

There are three text boxes:

IDP
RP-STS
Application

The article is somewhat confusing because the third text box refers both to a string to be passed to the application and the ADFS identifier of the application.

However, the use case we want i.e.

Identity provider security token server (STS) -> relying party STS (configured as a SAML-P endpoint) -> SAML relying party App

is supported.

I tried this with two ADFS - both v3.0.

So ADFS IDP = adfs-idp
ADFS RP-STS = adfs-rp-sts
Application ID = appid

which leads to:

https://adfs-idp/adfs/ls/idpinitiatedsignon.aspx
https://adfs-rp-sts/adfs/ls/
appid

and the URL is:

https://adfs-idp/adfs/ls/idpinitiatedsignon.aspx?RelayState=RPID%3Dhttps%253A%252F%252Fadfs-rp-sts%252Fadfs%252Fls%252F%26RelayState%3Dappid

So the user should authenticate on adfs-idp, be redirected to adfs-rp-sts, be already authenticated and then be redirected to the application.

However, I could not get this to work. I kept getting:

MSIS7001: The passive protocol context was not found or not valid. If the context was stored in cookies, the cookies that were presented by the client were not valid. Ensure that the client browser is configured to accept cookies from this website and retry this request.

If I left the third text box empty and removed the empty "RelayState=", I get:

https://adfs-idp/adfs/ls/idpinitiatedsignon.aspx?RelayState=RPID%3Dhttps%253A%252F%252Fadfs-rp-sts%252Fadfs%252Fls%252F

So the user should authenticate on adfs-idp, be redirected to adfs-rp-sts, be already authenticated and then select the application from the dropdown, This works.

For AD authentication, we can use the form at the top of the article i.e. using the loginToRp parameter.

This gives:

https://adfs-rp-sts/adfs/ls/idpinitiatedsignon.aspx?loginToRp=appid

So the user should authenticate on adfs-rp-sts and be redirected to the application, This works.

Or we can take a completely different approach (albeit with ADFS 3.0) as per:

ADFS 3.0: Playing with Authentication

where you can use:

Set-AdfsRelyingPartyTrust

where you can configure any of your relying parties to use specific claims provider(s).

Note:

Just to call out @RobM's summary from the forum question above:

"If you have a SAML IDP and a WS-Federation SP, you can use a URL constructed in the following manner to sign in:

https://corporate.adfs.com/adfs/ls/?wa=wsignin1.0&whr=https://identity.provider.com/&wtrealm=https://service.provider.com

If you have a SAML IDP and a SAML SP, the URL looks like so:

https://identity.provider.com/idploginpath/login?RelayState=RPID%3Dhttps%253A%252F%252Fidentity.provider.com

Note that with WS-federation, the flow is ADFS --> IDP --> ADFS --> RP

With SAML only, the flow is IDP --> ADFS --> RP"

Enjoy!

Thursday, January 07, 2016

OWIN : ASP.NET MVC application with multiple authentication options

There are many questions over at stackoverflow around this.

I have Forms Based Authentication (FBA) but I want to add other providers as well.

In WIF days, this was really difficult to do since WIF added events into the events pipeline.

With the advent of OWIN, this is a whole lot easier.

Assume you have ASP.NET Identity but you also want social (e.g. Facebook)  and WS-Fed and SAMLp. (p for the protocol as opposed to the token)

My starting point was Code! MVC 5 App with Facebook, Twitter, LinkedIn and Google OAuth2 Sign-on (C#).

I wasn't that interested in the social side - my interest was more the enterprise federation and I used Active Directory Federation services (ADFS) v3.0 as my IDP. This supports both WS-Fed and SAML.

The key point in creating the project is to leave "Change Authentication" as "Individual User Accounts". This gives you ASP.NET Identity as a starting point.

You need the following NuGet packages:

Microsoft.Owin.Security.WsFederation

Kentor.AuthServices

Kentor.AuthServices.Owin

The code in Startup.Auth.CS looks like:

// Uncomment the following lines to enable logging in with third party login providers

app.UseMicrosoftAccountAuthentication(
    clientId: "1234",
    clientSecret: "1234");

app.UseTwitterAuthentication(
    consumerKey: "1234",
    consumerSecret: "1234");

app.UseFacebookAuthentication(
    appId: "1234",
    appSecret: "1234");

app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
    ClientId = "1234",
    ClientSecret = "1234"
});

var adfs = new WsFederationAuthenticationOptions
{
    AuthenticationType = "Federation",
    Caption = "ADFS WS-Fed",
    
    MetadataAddress = "https://xxx/federationmetadata/2007-06/federationmetadata.xml",
    Wtrealm = "urn:owinmultirp"
};

app.UseWsFederationAuthentication(adfs);

var authServicesOptions = new KentorAuthServicesAuthenticationOptions(false)
{
    SPOptions = new SPOptions
    {
        EntityId = new EntityId("https://xxx/OWINMultiAuthWebApplication/saml/")
    },

    AuthenticationType = "KentorAuthServices",
    Caption = "ADFS - SAML2p",
};

Uri metadataURI = new Uri("https://xxx/federationmetadata/2007-06/federationmetadata.xml");

authServicesOptions.IdentityProviders.Add(new IdentityProvider(
    new EntityId("http://xxx/adfs/services/trust"),
    authServicesOptions.SPOptions)
    {
        MetadataUrl = metadataURI,
        LoadMetadata = true,
    });

app.UseKentorAuthServicesAuthentication(authServicesOptions);

You need the following includes:

    using Kentor.AuthServices;
    using Kentor.AuthServices.Owin;
    using System.IdentityModel.Metadata;

So there is just the one application but on the ADFS side we need to configure two RP; one for WS-Fed and one for SAML. Logically, ADFS thinks there are two separate applications.

There is no metadata so ADFS configuration has to be done manually.

ADFS WS-Fed

Set identifier to:

    urn:owinmultirp

Set endpoint to:

    https://xxx/OWINMultiAuthWebApplication/ or whatever for your app.


Set up claims rules as per IdentityServer : ASP.NET MVC application to idsrv3 to ADFS.

ADFS SAMLp

Set identifier to:

    https:/xxx/OWINMultiAuthWebApplication/saml/

Set endpoint as per:


Under the "Advanced" tab, set the "Secure Hash Algorithm" to SHA-1.

Set up claims rules as per IdentityServer : ASP.NET MVC application to idsrv3 to ADFS.

Just to reiterate - you must have a NameID claim for both protocols.

Login screen

Run up the application and you should see something like:


So now you have FBA plus a whole host of other options - all in the same application!

Enjoy!

ADFS : Support for SAML Scoping

With WS-Fed, the whr parameter allows you to specify the home realm and hence avoid the Home Realm Discovery screen.

What is the SAML equivalent?

There is relayState but relayState means two things:
  • IDPInitiated - similar to whr
  • SPIntitiated - used for context (similar to WS-Fed wtcx).
So for SPIntitiated, you're out of luck.

SAML also provides the Scoping parameter which performs a similar function.

An AuthnRequest using Scoping would look like e.g.

<samlp:AuthnRequest ID="_2528098e-4326-4fe2-83fd-72544c485420"
                    Version="2.0"
                    IssueInstant="2015-12-17T22:01:04.779Z"
                    Destination="https://xxx/adfs/ls"
                    ForceAuthn="false"
                    IsPassive="false"
                    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                    AssertionConsumerServiceURL="https://xxx/SAML/AssertionConsumerService.aspx?binding=urn%3aoasis%3anames%3atc%3aSAML%3a2.0%3abindings%3aHTTP-POST"
                    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                    >
    <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://xxx/</saml:Issuer>
    <samlp:NameIDPolicy AllowCreate="true" />
    <samlp:Scoping ProxyCount="10">
        <samlp:IDPList>
            <samlp:IDPEntry ProviderID="https://blah.company.corp" />
        </samlp:IDPList>
    </samlp:Scoping>
</samlp:AuthnRequest>


Unfortunately, ADFS doesn't support this in v2.0 / v 2.1 / v3.0.

Hopefully, it will in v4.0.

Enjoy!