Single-sign-on: Which direction should I go? - c#

I have a SaaS web application that caters to multiple education institutions. All clients are hosted in the same application/database. The application is currently written in C# for ASP.Net 4 Web Forms.
Currently my application uses a local/native database for user authentication/authorization.
Our clients are asking us to support single-sign-on where the client is the authentication provider and my application the consumer.
The problem is that the clients are asking for SSO via different protocols/mechanisms like Shibboleth and OpenID Connect. This means I need-to/should create a solution that works with all of these or that is at least extensible.
I came across Thinktecture's IdentityServer, which I think can abstract the various SSO mechanisms used by my clients and return to my app a claims based identity token that my app understands.
I'm struggling a lot with this concept though. Does this mean that my app redirects all authentication requests to the IdentityServer, lets IdentityServer handle the back and forth of say OpenID Connect, and then receives a token back from IdentityServer with the information I need about the user? How does the identity server know the realm of the user (i.e. so it knows which client auth provider to send the user to)? Does the IdentityServer need to validate the existence of the user in my app's local/native database? Can the IdentityServer handle both SSO and local logins?
Is a separate identity server the way to go? It seems like it would be, allowing my app to integrate with one point (the identity server). But, there's not a lot of documentation out there on Thinktecture's IdentityServer other than how to configure it. ADFS may provide a similar solution, but most examples out there speak to ADFS and Azure.
Lastly, I'm assuming that I'll still maintain local/native authorization data about each user as the 3rd party authentication provider can't possibly know the specific authorization needs of my application.
Any thoughts or suggestions out there?

Does this mean that my app redirects all authentication requests to the IdentityServer, lets IdentityServer handle the back and forth of say OpenID Connect, and then receives a token back from IdentityServer with the information I need about the user?
Basically YES. But it depends on how you set it up. Your page could call Authentication provider of the client if you have only one client or one authentication provider. Or you could set up your local IdentityServer (more extensible IMHO) and configure authentication provider of your client as another IdP (identity provider).
How does the identity server know the realm of the user (i.e. so it knows which client auth provider to send the user to)?
If you go with the second option then your app will redirect to IdentityServer and based on home realm it will be automatically redirected to IdP. If no home realm is specified by your application then IdentityServer will show all configured IdPs and user chooses what IdP to authenticate at.
Does the IdentityServer need to validate the existence of the user in my app's local/native database?
It depends on you. If you wish to verify the existence of the user in your local database then you may do so by extending IdentityServer.
Can the IdentityServer handle both SSO and local logins?
Yes, it can.
Is a separate identity server the way to go? It seems like it would be, allowing my app to integrate with one point (the identity server).
You can always use IdentityServer and integrate it in your local application. Or you can use Shiboleth as your local authentication provider. Both are implementing standards like WS-Federation, WS-Trust or OpenId and both are open source so you can extend/modify it to your liking.
But, there's not a lot of documentation out there on Thinktecture's IdentityServer other than how to configure it.
I can't really say how much documentation is there. But if you wish, NDC Oslo 2014 will feature 2 days of Pre-Conference Workshops where Dominick Baier and Brock Allen (authors of IdentityServer) will teach you everything you want to know.

Related

ASP.NET Core authentication/authorization with external OpenID Connect provider

I'm learning authentication/authorization in ASP.NET Core and got confused by which components I should use in my scenario: I have an SPA frontend and an ASP.NET Core API backend. I'm using a third-party OpenID Connect provider (like Okta) that only supports Authorization Code Flow, so I believe only the backend should talk to this external provider. The external provider only handles authentication, not authorization. Our application needs role-based access control.
Do we need Identity Server, considering authentication is already handled by the external OpenID Connect provider?
Do we need ASP.NET Core Identity?
How should we approach authorization in this scenario?
(I assume by "Identity Server" you're referring to IdentityServer4 or maybe even IdentityServer3)
Do we need Identity Server, considering authentication is already handled by the external OpenID Connect provider?
No.
People use IdentityServer4 when they want to self-host their own IdP (and of course, you can still federate with Google, Facebook, Apple, etc when you self-host your own IdP too), but when you use an IdP-as-a-service like Okta or Azure AD then you don't need to run your own IdP.
Do we need ASP.NET Core Identity?
No.
ASP.NET Core Identity is basically a utility-kit and time-saving library that (ostensibly...) makes it easier to crank-out the usual user-management features every traditional web-application needs: things like Registration, Password Resets, Automatic Lockout, Email+Phone verification, are all handled for you by ASP.NET Core Identity - including the user database via Entity Framework.
...but when you use an IdP-as-a-service then those same features (Registration, Forgot-password, etc) are supposed to be part of the service you're paying for (so you could either spend tens of thousands of dollars of dev-time building a self-hosted IdentityServer4 IdP - or you could skip all that and pay Okta merely tens of dollars per month instead. Guess which option your project budget manager wants to go with...
With ASP.NET Core Identity, or any self-hosted IdP where you have your own Users database tables, you'll need to query your own database to get user details (username, display-name, etc) (though normally you would store current-user-details in a structured security-token (JWT, SAML, ClaimsIdentity/ClaimsPrincipal, AuthenticationTicket, etc) when the user authenticates a new session so you avoid hitting your database on every request)...
... but when you use a non-local OIDC IdP (like Okta) those user details are stored and managed by Okta and also held by the client and then forwarded to you via the front-channel, or passed directly via the back-channel; those user details ("claims" in OIDC, known as "assertions" in SAML) are contained in 2 separate tokens: First, the id_token holds non-security-related user profile fields, like display-name and avatar image URI, while the access_token holds security claims and scopes (e.g. user role membership, account-is-locked-out status, etc). If your clients don't send you an id_token then you can always use the IdP's user-info endpoint to get the same data yourself (don't forget to cache it locally, otherwise it's even more expensive than hitting a local DB on every request).
SAML works very similar to OIDC, just with its own terminology for almost identical concepts. As an aside, I'm far more familiar with OIDC and OAuth2 than SAML (with which I have zero experience with), but AFAIK SAML uses a single token for both identity-claims and security-claims assertions whereas OIDC splits it up into id_token and access_token (I might be wrong on this...)
How should we approach authorization in this scenario?
Use declarative Authorization Policies which validate/authenticate based on specific user security claims in the access_token (do not use the id_token for authorization).
But while you can use the same C# Attributes for declarative authorization in your server-side code, be mindful that ASP.NET Web API clients and ASP.NET MVC browser visitors do things differently:
In ASP.NET Web API, clients might be an user-interactive smartphone app or desktop command-line program (think like Azure PowerShell) - or it could be a headless background-worker or daemon process. Most importantly: all these clients share the ability to store secrets like Bearer Tokens locally and securely as they're programs running with some kind of read/write access to their local-disk.
(While JS SPA clients use ASP.NET Web API, they don't have the ability to store secrets, so they're a special case that I discuss later on).
These clients all send their access_token in the HTTP Authorization: Bearer 3q2+7w== request header, and ASP.NET Core's built-in JWT authX features will compare the claims in the access_token with your declared policies and use that to accept or reject the HTTP request.
Whereas ASP.NET MVC clients are all web-browsers (Chrome, Firefox, etc) which use traditional HTTP cookie-based authX + sessions.
Browser clients have their access_token (and optionally their id_token) stored verbatim inside their ASP.NET security cookie - or the ASP.NET security cookie stores some short reference to a persisted token cached server-side to prevent cookie bloat.
This security cookie is symmetrically encrypted by your web-application and marked HTTP-only, so even hostile injected scripts (XSS, etc) in a browser-page cannot access and read those token strings.
Caution: because access_token and id_token blobs can be quite large (easily multiple kilobytes) you'll hit browsers' cookie length limits (4096 bytes?), so many web-applications just store all tokens privately (in Redis or memcached or something) and merely store a reference to the cached tokens.
When it comes to JS SPAs (Angular, etc) things are complicated: the client is ostensibly a user's web-browser (well, it's actually JavaScript code in the browser) and so it will make fetch-based requests in the background (instead of browser foreground "top level" Document requests) using an access_token in the Authorization header instead of using cookies (which wouldn't be accessible to scripts anyway, due to it being HTTP-only and encrypted, assuming the even were in the ASP.NET security cookie in the first place) - but because JavaScript clients simply do not have any way of securely storing secrets locally (window.localStorage is not private), so SPAs had their own separate OIDC flow: the Implicit Flow, but it's basically insecure - that, combined with browsers' disabling cross-origin cookies and other techniques that OIDC's Front Channel relies on basically means that SPAs now should not make their own fetch HTTP requests to external RPs (i.e. the backend ASP.NET Web API service for an SPA front-end) but should instead be served from a simple ASP.NET MVC which proxies requests to external RPs, and so the SPA actually uses Cookies, not Bearer Tokens - and means that the somewhat nice idea of hosting an SPA in static AWS S3 or Azure Blob storage is now dead.
So if you're planning on building an SPA, you really should give that
LeastPrivilege.com article a very good read so you understand how the browser landscape is changing (Dominick Baier is one of IdentityServer's creators and maintainers).
I will say that properly grokking OIDC is hard - I built a self-hosted IdP using IdentityServer4 and while I got something-out-the-door after a few months it still easily took me over a year to truly understand what was actually going on.

Can OpenID Connect in IdentityServer4 Request Tokens from Client rather than generate internally?

I am trying to better understand the OpenID Connect protocol with IdentityServer4, and am unclear on where I can find the answer to this question.
I like the idea of the Identity Server providing a Bearer Token at the end of authentication that includes claims on who the user is, and what resources they can access. However, I am a little uncomfortable in storing what Resources a user may have access to within the Identity Server environment. I would like this Authorization piece to be authored at my client application. Is there any type of callback in the OpenID callback that allows IdentityServer to call the client and request Authorization Claims from the client directly, rather than piece it together directly?
As an example, I am working on a product that will require a user to log in using either Windows Authentication, Username/Password, or a Google Account.
This client application will be hosted in a cloud environment, and will be hosted in the same domain as the Active Directory Server.
My thought was to create an Identity Server solution that is hosted internally, and to create a Client application that is hosted in the cloud.
When the user logs into the Client application, they would be redirected to the Identity Server to log in. The Identity Server would be hosted in the same domain as the Active Directory Server, so if there were Windows Credentials we could log them in automatically. Otherwise, there will be a login screen where they can enter a Username/Password, log in with Google, etc.
Once the user has logged in, the Identity Server would return a Bearer Token (a JWT probably), that has their claim information.
I can foresee this one Identity Server being used by many different Client application, because it would intrinsically allow Windows Authentication. For this reason, I am hesitant to store all of the API Resources the user can access within the Identity Server itself. My fear is that if I add a new permission to my client application (like a Report Admin claim), I would need to modify the Identity Server to include that new claim. And if 4 or 5 clients all use this Identity Server, this could get to be a bit onerous.
Instead, I would like to structure Identity Server so that when a user logs in for a particular client, Identity Server will make a callback request to the Client Application asking for the Claims that apply to that user. That way, the Client Application knows and cares about the User/Claim mappings, not the Identity Server. If I need to add a new Claim, I can do that within the Client application. If I need to map a user to a Claim, I can do that on the Client application. I don't need to do this on the Identity Server.
Is this functionality that already exists? If so, what do I need to be looking for? From what I can tell, it appears that the mapping between the User and the Resources they have access to all occur on the Identity Server.
The guide I am following is this: http://docs.identityserver.io/en/release/quickstarts/1_client_credentials.html#defining-the-api
Thank you for any guidance.
The Identity claims are not supposed to be used for Authorization, in fact Authentication and Authorization are two totally different concerns.
I'd suggest you watch this video which explains why and also provides an alternative PolicyServer that would answer your questions (great talk from the developers of IdentityServer).

Can a website authenticate against multiple ADFS servers?

We have an ASP.NET/MVC website that's using FormsAuthentication. As is usual, when the user tries to access a page, and doesn't have a valid FormsAuthentication cookie, IIS redirects him to the login view. When the user does a HttpPost to the login controller, our controller action makes a call to our WebApi webservice, which validates username, password, and customerid against a Sql Server database. If the authentication passes, the controller action sets a FormsAuthentication cookie, and redirects to the page the user had asked for.
Now sales is making noises about "Single Sign-On", though I'm not clear exactly what they mean by that. From what I've read, in the Microsoft World this usually means accessing MS's Active Directory Federation Services.
At this point I have almost no idea how this would work, but before I dig into this too deeply, would it be possible to put the authentication code within the WebApi webservice, where we could choose to validate against the Sql Server database, or against whichever ADFS server was appropriate for the specified customer?
Our problem is that we have I don't know how many thousands of users, working for some hundreds of customers. Many customers will not have ADFS running, and those who do will each have their own ADFS server.
Most of what I see with respect to Single Sign-On seems to involve doing browser redirection to the ADFS server, then redirection back, and looks to be avoiding login at all, if you're already logged in. I don't think we can do that, in our case. We can't know which ADFS server to redirect to, until we hit the database.
So, the question - is it possible to do ADFS authentication entirely from C# code in our WebAPI web service?
(One possible complication - the website itself has zero access to any database. The sole configuration setting in its web.config is the base URL of the webservice. Whatever authentication happens has to happen in the webservice, not in the website.)
First of all, "Single Sign-On" (SSO) is not limited to ADFS. It simply means that you type your credentials only once, and then all systems you access automatically "recognize" you; all subsequent authorizations request are transparent. For instance, if you have several web sites using Windows Authentication in your company Intranet (same AD domain), you have SSO: you authenticate once when you log in to your computer, and then your web browser authenticates automatically to these web sites using NTLM or Kerberos. No ADFS in this case.
What ADFS (and "Federation" more generally) allows, is SSO accross security boundaries. In Windows world, a security zone is typically created by an Active Directory forest; everything within this forest is accessible using SSO provided by Windows authentication. But as soon as you leave this zone (SaaS application, web site in another company network), you need another authentication protocol to perform SSO, and these protocols are implemented in ADFS.
Then about your particular problem:
What you could do is instead of using FormsAuth, you use AdfsAuth. When a unknown user accesses a page, he would be redirected to ADFS for authentication (using browser redirects as you correctly mention). To know which ADFS server should authenticate your user, you need a way to differentiate them indeed: a list of IP range per customer? a different URL per customer? If you don't have something like this, then the only way is to show them a list of choices such as: "I work for CompanyA", "I work for CompanyB", "I work for CompanyC", "I don't work for any of these companies and want to authenticate using FormsAuth."
In this case, what your WebApi web service has to do is: if I know which ADFS server to use, redirect the user there. Otherwise authenticate the user as usual using the database.
When you use AdfsAuth for a customer, your database is useless. You can delete all credentials related to this customer.
do ADFS authentication entirely from C# code in our WebAPI
Well it's possible to "re-implement" ADFS in your service, but you won't get SSO if you do that. When you use federation, your redirect the user to the ADFS server of his company. This ADFS server is in the same domain as his computer, so the user gets SSO here. Once again, your users can't get SSO if you authenticate them yourself, because your users are not in the same security zone as your site.
When authenticating to multiple identity providers, it is typical redirect to your own STS. So, in this case, you would have www.yourapp.com redirecting to sts.yourapp.com, which redirects to sts.somecustomer.com.
The specific tools to enable such a dataflow is the home realm parameter (whr), and the AD FS Powershell API (to allow IDP maintenance).
Your RP-STS acts as the trust-point for the app, and manages selection of the appropriate IDP. One RP-STS, many IP-STS's. Each of your Customer's IP-STS gets set up as a Claims Provider Trust in AD FS.
As always, Vittorio has already covered the subject better than I could.

Asp.net authentication in its own web application

I currently have two web applications, one of which is a web api, which both have it's own implementation of ASP.NET Identity for authenticating users (first one is cookie and second one is bearer tokens).
I need, in the near future, to add a third web site to the family and was thinking to move the authentication logic in a standalone web application which could serve all the orhers.
For example: instead of authenticating to www.mysite.com and/or api.mysite.com, I would like that both the applications could authenticate against a new website, like e.g. login.mysite.com, as microsoft does.
How can I achieve something like this? Does somebody has any direction to start with?
What you need is an Identity Server which will authenticate the requests and give you back an access token. You can then use the access token to make subsequent requests to APIs and websites etc. As long as the access token is valid you will be able to make requests. There is great .net based open source solution available for Identity Server. Have a look at Thinktecture. You can download the source and demo projects to try yourself.

Posting a SAML token to ASP.NET MVC website

I have a claims aware MVC website setup using the Thinktecture Identity Server. I now have a requirement to allow a 3rd party to access certain parts of the website.
Is it possible to programmatically authenticate with the Identity Server and post this to the website so that the user at the 3rd party is not required to manually go through the normal login process?
I have previously used the identity server to obtain a SAML token for the purpose of making WCF calls, I was wondering if it would be possible to re-use some of this approach?
The complications arise from the fact that the 3rd party are using a desktop based Java app with some browser component built in for accessing the MVC website. Users are already authenticated with the desktop app so we don't want them entering credentials again to view these web pages.
Hmm, tough one to solve in a secure fashion. Basically I don't trust desktop apps ;-)
You might be looking for some sort of federation. Basically it is about letting your Identity Server trust the 3rd party (by means of signature). Your Identity Server would then use a SAML token from 3rd party as login information and generate a new SAML token (signed by you) to forward to the the MVC web application. I think Dominick has a video or two about it, but not sure it meets your requirements. Take a look at https://github.com/thinktecture/Thinktecture.IdentityServer.v2/wiki
The Java desktop app probably cannot create a signed SAML token, so you could consider using "something else". Preferably something signed from where they logged in the first time.
You should avoid having your web app trust anyone but your own Identity Server / Identity Provider.
Hope this helps? Happy to help where I can :-)

Categories

Resources