Identify User with OpenID Connect with multiple Providers - spring-security-oauth2

I'm writing a web application with spring boot and want that the user is able to tell me what his identity provider is. In a same way as I can do it on Stackoverflow.
How can I identify a user in a unique way? I already read that I should use the sub/Subject for distinguishing users. Is this unique when using multiple providers?
My fear is that a user provides a malicious identity provider which then tells my app he is a different user.

How can I identify a user in a unique way? I already read that I should use the sub/Subject for distinguishing users. Is this unique when using multiple providers?
You'd store the combination of (iss, sub) as an identifier that is globally unique. As Kavindu mentioned already, the sub claim by itself is only locally unique.
My fear is that a user provides a malicious identity provider which then tells my app he is a different user.
There are two ways of using "multiple providers" with your app, via:
a set of trusted IdP's
any IdP
If someone's real identity is important to you, then you can choose the providers you trust to provide someone's identity details. People then can only sign in via one of the providers in your list.
But if it doesn't matter that much (normal username/password registrations also don't provide any guarantees), then you could also choose to let people login with a provider of their choosing. The correct provider may then be discovered from the user's "handle" via OIDC Discovery.

Q : How can I identify a user in a unique way? Is this unique when using multiple providers?
According to OpenID Conenct specification, "sub" claim is locally unique. Following is the extraction from specification which highlight this (reference),
Subject Identifier. A locally unique and never reassigned identifier within the Issuer for the End-User
So when you are dealing with a single identity provider, "sub" claim is unique. But that does not hold for multiple providers.
Q : My fear is that a user provides a malicious identity provider which then tells my app he is a different user.
I doubt about this scenario. Does your application allow end users to register different identity providers as they want ?
In OpenID Connect, there's a application registration step. Your application need a client identifier. Also registration process involve redirect URL registration. All these are done in registration step. Without these, OpenID Connect will not function.
Adding to that, different providers behave differently. For example, though "sub" is the standard claim to communicate end user identity, a provider may use a custom claim to define a specific user identity. This is allowed by OpenID Connect specification. So your application must only support known, well established identity providers which you know at the application design time.

Related

OAuth2 protected API. How to allow customer's to SSO using its own authorization server?

I have an Angular Single Page Application (SPA) talking to my ASP.NET API.
The API is protected by my own Oauth2 server (IdentityServer4).
One of my customers (let's call him X) wants SSO: Their users on my platform would sign in on their server instead of using the login form in my app that connects to my IdentityServer.
Each customer has its own subdomain for the Angular SPA (e.g. x.myapp.com). Therefore I can easily redirect X's users to their server's authorization page to approve my API, based on the hostname.
However the API itself uses one common hostname for all customers(api.myapp.com). Customers are distinguished by the Origin header of the API call (x.myapp.com) during the login call (and a few other unprotected calls) and by the Bearer token for protected calls to the API.
How does my API introspect the Bearer access token? Who should know which server to query ?
Is it the responsibility of the API server? Or can I tell my IdentityServer about X's oauth2 server ?
X's users would also be defined on my platform since we need specific info (such as config of roles on the platform). My current setup implies specific claims (such as user id) that allow my API to know what the user can do. Obviously, X's server will not provide the same claims. How can I connect the dots ? e.g. get some standard claim from X's server (username, email, whatever) and match it to my list of users.
Note: This question is similar but the answer is not accepted and seems to imply that the provider of both identity servers is the same (not the case here).
Formatting my comments as an answer:
From reading your question it's pretty much clear to me that you could benefit from what is know as Federated identity.
As you said, one of your customer want to achieve SSO - They want users to login using their existing accounts and be able to user your systems normally.
Since you already have an IdentityServer in your domain, what you can do is delegate the login part to the customer's side (whatever they do it). This is illustrated in the Identity server documentation Federation Gateway.
Basically, the approach is that upon hitting the "login" button in your front-end, you would redirect the users to your Identity Server passing some special params (prompt and acr_values for ex) which in turn, tells identity server to redirect the user's to the external Identity provider (the customer's). After a successful login, you have a chance in Identity Server to augment the claims, maybe using something they returned or anything really. Then the process is as normal - you return a JWT Token generated by your Identity Server
The benefit of doing this is:
Your SPA/API doesn't have to change. You will still work with your own bearer tokens and can continue doing authN/AuthZ as before.
You have a chance to add claims that might indicate where this user is coming from if needed
If your customer's server changes, you don't have to worry much, apart from maybe some tweaks related to returned claims
They don't necessarily need to use OpenId/OAuth on their side for this to work
Useful things you probably will need is some params to pass during the call to the authorize endpoint in Identity Server. (acr_values and prompt).
You can also check this in the quickstarts, by looking at Sign-in with external providers (which is pretty similar to what you want)
Now to your individual points:
Your Identity Server should be the "bridge" between you and the customer's "identity provider".
Upon a login from an external provider (X), you need to somehow identify the user on your platform. You could use email or, even better, if X is already using OpenId/OAuth they might give you the sub claim which is the user id on their side. At this point you need some sort of agreement with them otherwise this might be flaky/unreliable for both sides.
In a more "advanced note" you could also add to your tokens some sort of claim that tells you who is the source provider of this user. Here the source provider would be X. This is useful because you might want, for example, configure allowed identity providers in your app, or maybe enable features only for certain providers. Like, ppl logging in with Google might only see certain parts of the app.

Should I use claims to reflect an authorized user with no special roles, and if so, how?

I'm currently using ADFS and claims-based identity in ASP.NET web applications to convey informations such as a user's name or identifier.
In addition, inside applications a ClaimsTransformer adds a couple more claims that are relevant to that app. Such claims include applicative roles (Admin, Validator, etc.), whereby the user can't see some pages unless they have the right role.
However, just because you have no role doesn't mean you can't access the application at all. Vanilla users that aren't part of a specific group are sometimes also able to see some pages. Besides, we have a few web applications where roles aren't used -- it's just a matter of whether you can get in or not. We call this "access", which is different from "role" or "permission".
Access rights to applications are stored in a separate database. You can have access to an application if an admin manually granted it to you or if you satisfy a set of rules calculated by another system.
How would you materialize access in terms of claims? A claim type like HasAccess with a true/false value sounds weird and vague.
Is claims-based identity the right place to look at? The idea is to insert that information precisely enough in the authorization chain that every auth cookie doesn't have to carry a list of all accessible applications for the user, but early enough in the process that an accessibility check doesn't have to be done at each http request.

Retrieving all users and roles in a .NET Web Application through ADFS

We have a hosted .NET web application (Windows Server 2012 R2 environment) and we need to provide Single sign-on (SSO) to users from a corporate LAN environment. We have used ADFS to enable SSO and it is working as expected thus when a user hits our web application login page URL he is authenticated against ADFS and is automatically logged in to the application.
We have an additional requirement where we need to obtain a list of all users, their groups, email addresses some additional information periodically from their Active Directory so that this information can be bulk loaded into our web application however since ADFS is implemented we do not have direct access to the Active Directory.
Is it possible to connect to ADFS and obtain a list of all users, their email addresses etc. programmatically?
If the above is not possible then what is the recommended approach for this kind of a setup?
Thank you.
No, this is not possible. There is no such API because with SAML and WS-Federation, users can come from anywhere. This does not have to be AD, technically it's possible create a "Log in with Facebook" implementation.
What would you need the information for? The user's claims contain all information which you might need (user name, e-mail address, group memberships).
If you really need that information about all users in your application, perhaps ADFS is not the solution you are looking for.
As Alex mentioned above - the way it works, ADFS does not provide any way of importing data from the AD or other trust stores. It just gives you the information that are carried over with the token.
In case you need more information, you should extend the number of claims being issued by ADFS. You can then collect the information - when the user comes for the first time, use the data from the token and fill the profile. If it is returning user - update the information if necessary.
The other solution (but I wouldn't say it's recommended - rather a workaround) would be to implement custom solution for importing information from AD to your application. I'd say it's fair as long as you use your local AD for reading this data. In the moment you decide to extend the access to third party (e.g. partner company), which might be using different identity provider, which doesn't have to be backed by Active Directory any more - you find yourself in tough spot.

Are ASP.Net Identity Claims useful when NOT using OAuth/external authentication providers?

Everything I've read about claims-based authentication is essentially about "outsourcing" your authentication process to a trusted 3rd party.
See:
Explain "claims-based authentication" to a 5-year-old
Why Claim based authentication instead of role based authentication
Obviously this lends itself well to using something like Facebook or Google to authenticate. But what if there is no 3rd party? What if you just need users to authenticate against an internal database? For example, in a corporate setting. Is there any reason to use claims over plain old roles? If so, some concrete examples would be helpful.
What I know about claims so far:
I understand that claims are key/value pairs rather than booleans like roles.
I understand that claims can store roles.
And if I understand correctly, claims get stored in an authentication cookie (maybe this is key - fewer database calls vs. roles?).
There are numerous reasons, including these you mentioned.
Another reason is that forms authentication module is incapable of handling too large cookies. Just add few hundred roles and exceed the maximum allowed cookie size (4kb) and you are out of luck. The session authentication module that handles claim cookies automatically splits too large tokens into multiple cookies. And if you don't want to have multiple cookies, just a simple switch to "session mode" automatically stores the large token in the session container and the cookie only contains a small bit of information to reflect that.
Yet another argument for the claim-driven cookie is that you can handle any custom data, including tenant name (in a multitenant application), name of home organisation, age, whatever you can need somewhere later. Forms cookie has the custom data section but it is just a string so that you need a custom serializer to have structured data here.
All this is done by the session authentication module and, frankly, it outperforms the forms module easily then. Switching your forms authentication to the new module is also easy, I've blogged on that some time ago (other people blog about it also):
http://www.wiktorzychla.com/2012/09/forms-authentication-revisited.html

How to integrate AD authentication + SSO with exsisting Forms authenticated Saas web application

We are running a Saas ASP.NET 3.5 Web application using Forms authentication on a IIS 7.5 public server with protected content for thousands of users. We also have some subapplications running ASP.NET MVC 2.
Usernames and passwords are stored in our database and every user has roles and groups attached, with privileges and access rights defined.
Now we have been asked to also facilitate for simple SSO login via Active Directory so that users do not have to enter username and passwords twice to login. These users will originate from different networks and domains.
No user "sync" should take place from our servers to LDAP serves. We are not sure that any communication with LDAP is needed since all users will be created in our system and maintained there. Forms authentication will be used for most of our users.
From here on we are unsure which is the best path to choose. For our scenario what would be the "best practice" way to proceed?
The simple answer is SAML. It is considered the "best practice" and many large SAAS providers support it.
SAML protocol defines the single sign on flow between multiple systems. It establishes trust between systems using certificates. Your application accepts an assertion containing attributes (user id, name, email address, etc.) from other systems. Your app will map the user into your user store.
In .NET world there are several options. You can find a library that implements SAML (ComponentSpace has one) and hook it into ASP.NET authentication. You can create your own using Windows Identify Framework (WIF). Here's the boatload of WIF videos http://www.cloudidentity.com/blog/2010/06/23/ALL-WILL-BE-REVEALED-7-HOURS-RECORDINGS-FROM-THE-WIF-WORKSHOPS/. You can try IdentityServer http://thinktecture.github.io/
Depending on how secure your app must be, you can opt for a simple option of passing user id from trusted networks using a simplified method. I've seen apps that allow user id to be sent via URL parameter or form field. Of course, this is horribly insecure, and you are taking on more risk, because the trust between two networks is not cryptographically enforced. You can mitigate it somewhat by checking referrer string or IP address (if you can isolate IP range of a corporate network for example). But you are still open to spoofing because any user can impersonate others by simply replacing user id within HTTP request.
It probably doesn't answer your question fully, but hopefully points you in the right direction.
I recommend looking into ADFS 2.0 it is very powerful in terms of claims mapping and works with AD: http://msdn.microsoft.com/en-us/magazine/ee335705.aspx
What you would make is a token consuming portion of your app that would receive and parse the final claims returned to your web server after the authentication loop.

Resources