How SAML 2.0 Authentication Contexts works in following scenario? - saml-2.0

I would like to build SAML request in such a way that it support both Windows based Authentication and Form Based Authentication.
Scenario 1
ADFS Authentication policies are set to Windows based Authentication and Form Based Authentication.
If both authentications are set first priority should be given to Windows based Authentication.
Scenario 2
ADFS Authentication policy is set to Form Based Authentication.
In this case Form based authentication should be done.
My Question is how to set RequestedAuthnContext in SAML AuthnRequest so that above scenarios are covered.
Which Comparison Type should be used (MINIMUM,EXACT,MAXIMUM,BETTER)?
This is code snippet
RequestedAuthnContext requestedAuthnContext = SAMLUtils.buildSAMLObject(RequestedAuthnContext.class);
requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.MINIMUM);
AuthnContextClassRef windowsAuthnContextClassRef = SAMLUtils.buildSAMLObject(AuthnContextClassRef.class);
windowsAuthnContextClassRef.setAuthnContextClassRef("urn:federation:authentication:windows");
AuthnContextClassRef passwordAuthnContextClassRef = SAMLUtils.buildSAMLObject(AuthnContextClassRef.class);
passwordAuthnContextClassRef.setAuthnContextClassRef(AuthnContext.PPT_AUTHN_CTX);
requestedAuthnContext.getAuthnContextClassRefs().add(windowsAuthnContextClassRef);
requestedAuthnContext.getAuthnContextClassRefs().add(passwordAuthnContextClassRef);
My Question is how to set RequestedAuthnContext in SAML AuthnRequest so that above scenarios are covered.
I tried all Comparison Types. But Expected scenarios are not working.

The SAML spec (Core with errata, section 3.3.2.2.1) says this about RequestedAuthnContext element:
If ordering is relevant to the evaluation of the
request, then the set of supplied references MUST be evaluated as an
ordered set, where the first element is the most preferred
authentication context class or declaration. For example, ordering is
significant when using this element in an AuthnRequest message but
not in an AuthnQuery message.
The part about evaluation as an ordered set was clarified in the errata revision of the spec
The comparison rules:
If Comparison is set to "exact" or omitted, then the resulting
authentication context in the authentication statement MUST be the
exact match of at least one of the authentication contexts specified.
If Comparison is set to "minimum", then the resulting authentication
context in the authentication statement MUST be at least as strong (as
deemed by the responder) as one of the authentication contexts
specified.
If Comparison is set to "better", then the resulting authentication
context in the authentication statement MUST be stronger (as deemed by
the responder) than any one of the authentication contexts specified.
If Comparison is set to "maximum", then the resulting authentication
context in the authentication statement MUST be as strong as possible
(as deemed by the responder) without exceeding the strength of at
least one of the authentication contexts specified.
Our translation of your scenario:
Windows auth should take precedence over form-based auth
Form-based auth should be the fallback
The most spec-compliant way of implementing the request is
requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
...
requestedAuthnContext.getAuthnContextClassRefs().add(windowsAuthnContextClassRef);
requestedAuthnContext.getAuthnContextClassRefs().add(passwordAuthnContextClassRef);
If your ADFS policy has Windows auth and Form-based auth as selected options, the IdP should select Windows-based auth since it's referenced first in the authentication request. If your ADFS policy contains only Form-based auth, the IdP should go with that since it still exactly matches one of the two auth contexts in your authentication request.
If you did NOT have control over ADFS setup and you could not guarantee the auth options chosen in ADFS policy, then a safer implementation would be to use Minimum as the comparison strength:
requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.MINIMUM);
...
requestedAuthnContext.getAuthnContextClassRefs().add(windowsAuthnContextClassRef);
requestedAuthnContext.getAuthnContextClassRefs().add(passwordAuthnContextClassRef);
So far we've talked about the spec perspective but does ADFS respect the spec and implement it correctly? ADFS 2.0 claims to do so with a short list of supported auth contexts. On ADFS 3.0 there's at least one report of this feature being broken (allegedly). Verification of this scenario on ADFS 4.0 and 5.0 would be an interesting bullet point, perhaps someone can comment.

First of all, the Authentication Context is an abstraction.
Which actual Authentication mechanism is really used by the IdP is totally up to the IdP.
Even if you request 'SPNEGO Kerberos' ("urn:federation:authentication:windows") the IdP may use a totally different mechanism.
Furthermore there is no specification that defines an order upon Authentication Context Class Refs ... again it's up to the IdP implementation to decide which mechanism it considers stronger than another.
From that respect you may use 'MINIMUM' as comparison. 'EXACT' may reduce the probability for successful SSO flow.

Related

Use Microsoft OAuth to authenticate our own operations

We have built a React web application that authenticates users with Microsoft via OAuth ( #azure/msal-browser package). Users are redirected to Microsoft auth page during signin, and then redirected back to our site once authentication has completed successfully. This is all working as expected.
However, we are in a position where we wish to add a custom PIN mechanism to protect some of our workflows. PINs will be encrypted/salted and stored in our own API, along with the existing mapping between Microsoft/Azure users and our own user state/records. In order for users to change/reset their PIN, we want to force them to reauthenticate with Microsoft before changing their PIN. I have not dealt with OAuth2 in a while, and am not entirely certain how this might be possible (since current auth workflow does not involve our server at all).
Ideally, users would navigate to the "Reset PIN" page and initiate the workflow. If possible, authentication would be handled through a pop-up (so as to not lose page state), which I think is possible as per documentation? If this is not possible, even a redirect to the same page with a specific query parameter (to indicate post-authentication) could work. The user would then be able to change/confirm their new PIN. At this point, a request would be sent to our API, including both the PIN and something from Microsoft that would allow our server to validate that the user did indeed just re-authenticate (proving they can change the PIN).
The primary sticking point is that our API must be able to verify that the user has just re-authenticated with Microsoft! One way to think about it would be that if a user gained temporary access to an unlocked authenticated workstation, they should not be able to perform restricted actions (as they do not know the PIN), nor be able to change the PIN (as they do not know the user's credentials). Yes, yes, I know physical access is effectively a compromise to any security, but this is a higher-level requirement...
I am guessing this workflow is possible, but am not certain how it might be possible. Would we use a different authentication workflow for this particular use case? Any pointers on a workflow we could investigate or use for this?
I can give you some info about how this should work. This type of flow can require a high level of extensibility in the authorization server though.
CLIENT REDIRECT
The client can send the OpenID Connect acr_values parameter, indicating the assurance levels it requires. In your case you would send two values.
CUSTOM AUTHENTICATION FACTOR
The user will perform standard authentication first, eg passwords. Then you would implement a custom second factor that uses the user identity and some custom logic to call your custom API.
ACCESS TOKENS
After authentication has completed, the authorization server will issue access tokens to the client. High privilege claims should only be included when strong authentication has been used. Some kind of scripting would be used here, that can read authentication result attributes.
API AUTHORIZATION
APIs should receive short lived access tokens. If it contains the high privilege claim, you know that your PIN requirements have been met.
CAN YOU SIMPLIFY?
It feels suspect to do your own PIN management. Can you outsource this, eg use a second factor of time based one time password (TOTP) instead? Mobile platforms such as the Google authenticator app provide tested solutions.

External login only, no local users - is this a supported option in IdentityServer 4?

I am planning on using IdentityServer4 exclusively as an OIDC proxy to implement a single sign-on scenario relying on an external login provider (ADFS3 for starters, but there might be others in the future). Put it another way, no local users will ever exist, and neither do I expect to have any login or consent screens.
I've been reading the docs and studying the quickstarts source code, and even though I believe now I have more or less complete picture of how external login works, there's this one thing that I am still not sure about.
Namely, would it be safe and reasonable to do the following:
Do not implement any user store (neither ASP .NET Identity nor even an in-memory one);
Ignore the EnableLocalLogin client setting and always assume it's set to true;
Always force ExternalLogin from the login page?
A side question: are there any pitfalls if the primary client would be a JavaScript SPA? I do know this type of client is generally supported by IdentityServer, but it's the chain of redirects involved in the external login scenario makes me a bit worried.

Saml 2.0: Why is NotBefore in the schema for SubjectConfirmationData if it is not allowed?

We have several service providers using Saml 2.0 for single sign-on and they are all working fine. Our most recent addition is balking because we send a NotBefore attribute on the SubjectConfirmationData element. As far as I can tell, this is part of the xsd schema for Saml 2.0 (https://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd) but then it is marked as MUST NOT in this profiles PDF (https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf).
What am I missing?
As you figured out, the Web Browser SSO profile - which is the one you are using to provide single sign on for your users as an Identity Provider - dictates that you should not set NotBefore. The profile sets further limitations to the schema defined for Assertion to be used generically, which is totally fine.
The service provider at hand strictly checks compliance and thus rejects your Assertion. The fact that it has been working so far probably means that the other service provider implementations were more lenient.

SAML 2.0 IsPassive option

When working on an Apache Tomcat SAML 2.0 based single-sign-on (SSO), I came across the property named 'IsPassive' under SAML 2.0 Authentication Requests. The SAML 2.0 spec introduces this as follows:
IsPassive [Optional] A Boolean value. If "true", the identity provider
and the user agent itself MUST NOT visibly take control of the user
interface from the requester and interact with the presenter in a
noticeable fashion. If a value is not provided, the default is
"false".
What is the most accurate meaning or example of this definition in terms of a single-sign-on scenario? Does this property have a connection with active and passive profiles in single-sign-on?
First, this has nothing to do with the Active or Passive SSO. Typically "Active" refers to Web Services based SSO (I usually think about desktop client apps for this) while "Passive" more typically refers to Browser-based SSO.
Second, by sending IsPassive=True, the SP is essentially telling the IDP, "Authenticate this user only if you can do it without have the user involved." I think the most common methods for Web SSO might be Kerberos (Integrate Windows Auth) or x509. Alternatively, if the IDP has already authenticated the user and the user has a valid session that can be re-used for the given SP AuthnRequest, then that qualifies as meeting the IsPassive=true requirements IIRC.

Enabling multiple tenants using OWIN Active Directory bearer tokens

I've recently started playing with Azure Active Directory to authenticate users against my website built on AngularJS.
Using blogs and sample code on GitHub, I've gotten it working with single-tenant using a combination of ADAL.js and Katana's Bearer Token AD integration.
However, I'm now running into some issues with supporting multiple tenants.
I've got a page set up that displays the user as ADAL sees them (found through the root scope's userInfo), as well as makes a call to my server that gets picked up by OWIN, and serializes context.Authentication.User.
Client-side, everything seems to be working properly. I can log in with any of my tenants, and it gives me the object I'd expect (with isAuthenticated: true, username populated, and all sorts of properties on profile describing the user, login, and tenant).
This is accomplished client-side by leaving off the tenant argument to my adalAuthenticationServiceProvider.init call, as described in the documentation.
Server-side, however, the UseWindowsAzureActiveDirectoryBearerAuthentication method doesn't like having no value for Tenant (in that it throws an exception). I've tried a few values for this, including the tenant with which my app was originally registered and, my logical favorite, "common," but no matter what I put in there (unless it's the tenant I'm trying to log in with, and if my ADAL is set up with that tenant), it seems to just skip over this.
For what it's worth, an actual API call is failing on the [Authorize] filter and returning a 401, which tells me this isn't an issue with my OWIN interceptor.
How can I tell UseWindowsAzureActiveDirectoryBearerAuthentication to support multi-tenant authentication?
I figured this out while writing the question. I think. But I spent all day on this finding almost no documentation on the matter, so I figured I'd post it anyway.
My solution (found through yet another blog post) was to include ValidateIssuer = false as a parameter. This makes sense, since we no longer want to validate that the tenant giving us a token is the one that we've listed.
Here's my code that solved the problem.
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
ValidateIssuer = false // This line made it work
},
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
Tenant = "common" // I don't know whether this has any impact,
// but it's a required parameter regardless.
});
I'd love if someone else wanted to correct me if this has any unforeseen circumstances--it's a tad daunting flipping a "validate" switch to off when you're working on authentication. But I think this all makes enough sense.
When you are developing a multi tenant application, you can no longer rely 100% on the default authentication logic. The default authentication logic assumes that you declare the azure AD tenant form where you want to receive tokens, and will enforce than only tokens form that tenant are accepted. This is done by examining the metadata document associated to every tenant, which contains (among other things) the identifier of the tenant itself - that identifier must be present in the token you receive, in the iss claim: any other value means that the token comes form another tenant, hence it must be rejected.
By definition, multitenant applications must accept tokens from multiple tenants. This is done by using a parametric endpoint (the common endpoint, see this post) which allows you to "late bind" which tenant will be used to issue a token. However the common endpoint will serve a generic metadata document, which cannot contain a specific iss value: instead, it contains a placeholder that at runtime will always be substituted with the issuer identifier of the tenant you actually got the token from.
This means that in multitenant apps you have to take over the tenant validation logic. If you are just debugging you can turn it off, as you appear to have done - that will prevent the default issuer validation logic from kicking in and refusing the incoming token because its iss value does not correspond to the placeholder found for common. In more realistic cases, however, you will write your own logic in the TokenValidationParameters.IssuerValidator delegate. For example, you might want to compare the iss value in the incoming token against a list of tenants that bought a monthly subscription to your service. HTH

Resources