I am developing an SPA and would like to have SSO.
As I understood so far, OAuth2 with OIDC is the best solution for SPA SSO.
Better than, for example, SAML.
What I didn't understand so far is how to use authorization token in SPA's JS code to handle authorization on various resources of SPA. For example, I would like the users with a role 'buyer' to have access to the shopping history tab, where other users won't have access to.
Should I parse access token obtained from Authorization server in JS code and check whether a user has an appropriate role to see the tab, or should this decision be made on server (API) side, in which case SPA's code would just read the answer from API and based on that customize UI?
In case of the first approach, is there any standard way of doing the checking (in form of some JS library)?
When it comes to authentication, what is the better approach (more secure, etc):
to let SPA (at that point already loaded in the browser) do the authentication flow and based on result let the user use it's protected functionalities. This is pseudo authentication actually since the code is in the user's browser and means the user is authenticating himself to the code in his hands i.e. to himself. Does this authentication make sense at all?
require the user to authenticate himself in order to be able to even load the SPA in his browser. This is probably not SPA architecture then since backend which serves the SPA should be able to create a backchannel with the Authentication server.
According to user description, your application must vary depending on user type. If this is the case I would suggest you to use a backend for authentication and decide application content to be served from the backend. Otherwise, as you have figured out, running authentication on browser and altering user view is not secure.
IMO this not necessarily break SPA architecture. What you are doing is altering what you server based on tokens presented to you. Also, maintaining a session will be required with this approach. And SPA's calls for backend will require to contain this session to obtain contents.
As soon as the User is logged in, you would request for authentication and based on his UserId, and the role he belongs to you should receive all the permissions that User is entitled to.
You convert these permissions into claims and can send them back to UI and use it appropriately to show the features accordingly.
You also enforce same on the server side api to prevent any unauthorized access besides from your UI.
Related
I have a ASP.NET Core 6 web app. I'm using Identity for an authentication. It is in its simplest form - users need to put credentials in the login page to access parts of aplication.
Now I need to expose one endpoint. It should be accessible by application from a different server. Therefore a solution with redirection to a login page and storing credential in caches doesn't make sense (keep in mind I want to keep it, just add some filtering on a particular api).
Instead I would prefer to allow hitting this enpoint with some kind of token. Another solution that comes to my mind is to whitelist the server of the application not to require authorization.
I don't know which of these are possible or recommended. Will gladly hear your advices.
I have my REST API server which stores user data and handles all the requests. The front-end lives on another server and then there's also a mobile app. I would like to integrate OAuth2 but have my doubts on what grant type to choose. On one hand, the ROPC grant type is best in my situation since I don't allow any third party apps, and I don't want user to be redirected anywhere and the user never gets to use my endpoints directly, only with some kind of interface (front-end, or mobile GUI). So, what are the possible options here?
Normally Authorization-Code Grant is the way to go. I assume you are asking this question, because you heard that ROPC is rather unsafe to use and should be avoided when possible? And that would be true.
Use Authorization Code Grant. Better: use Authorization Code Grant with PKCE. (PKCE is mandatory on mobile Apps and a good practice on WebApps too)
I know that the Authorization Code can seem complicated at first, but it is really the way to go with OAuth.
Okta Blog to PKCE
I have a website that exposes a public API. I want to secure this public API using OAuth 2. In order to minimize the number of code paths to maintain, I want to refactor my website to use the OAuth 2-secured public API endpoints.
The way I intend to do this is to register an OAuth 2 Client in my server as "my website", and then have that fetch a short-lived token. I see 2 problems with this approach:
My client would have to effectively have every scope, since the
website encompasses every possible action. The API is just a subset
of this (though I'm hoping to change that). The second issue is
security and caching of the token. The token would live for an hour.
If the user refreshes the page, do I fetch another token? If I store
it locally in a cookie or localStorage, is there a security
vulnerable of some sort?
Let's say I register a different OAuth Client for each page of my UI. The would make it so that the token in #2 would have limited scope if stolen, but it gets extremely tedious.
The alternative is to not use the public API on my website, and protection relies on CORS. Malicious attackers cannot access these endpoints because they are only allowed to come from the domain the user is on (and things like nonces).
Sounds like you want towards a model based on a Browser UI calling APIs directly, which is certainly the most attractive architecturally.
This may be the first step to a Single Page App architecture, which tends to provide the simplest and cleanest solution.
See Open Id Connect for browser apps for latest standards.
In an SPA the UI is cookieless and it is common to store short lived access tokens in HTML5 session storage, which means the user can refresh the page ok
It is true that all scopes are retrieved after login, but if you keep scopes simple and authorize in your API based on user rights you can mitigate that
The above token storage is the default behaviour for the certified OIDC Client Library and widely used.
There is a cross site scripting risk, that malicious content in your browser tab can get a token and call the API - but you should be protecting against this anyway.
Older solutions such as use of auth cookies tend to have their own (and bigger) risks such as cross site request forgery, where any malicious content in any browser tab can send the cookie to your API.
Evaluating whether to use HTML5 storage of tokens is about more than just the technical mechanism - it is about acceptable trade offs around usability and what can be done with the token. My blog post on UI token storage drills into this.
If it helps, my blog also has quite a few posts and code samples on SPAs, in case it is of interest.
I have a single realm with 3 single-page applications and a shared backend. I want to restrict the access to one of the SPAs so that users without a specific role can't log in.
But once you create a user in the realm, he can log in to every SPA client. I can restrict the endpoints of the backend but I don't want to programmatically reject the user in the specific SPA but automatically on the login page.
I tried to use client roles which don't seem to have an effect in this case. The only solution I have found so far is to create separate realms which I think is conceptually the correct way but unfortunately brings up some practical issues, e.g. the administrators of one realm must be able to manage (CRUD) users of another realm which seems fairly unintuitive.
users without a specific role can't log in - it isn't good requirement. How system will known if user has a specific role without log in (authentication)? Keycloak provides Open ID Connect SSO protocol, which is designated for authentication. After successful OIDC authentication is token generated, which may contains also user role. So only then authorization can be applied. So let's change requirement to: users without a specific role can't access SPA, which better fits into OIDC concept.
The mature OIDC SPA libraries offer authorization guard (name can differs, it is some kind of post login function), where authorization can be implemented. Authorization requires to have a specific role in the token usually, otherwise user is redirected to the custom route, e.g./unauthorized. That's the page, where you can say a reason for denying access. Common use case is also customization of the app based on the user roles. For example users with admin role will see more items in the menu than standard users - that's also kind of authorization. Random example of SPA library with authorization guard (I'm not saying that's a best implementation) - https://github.com/damienbod/angular-auth-oidc-client/issues/441
Keep in mind that SPA is not "secure" - user may tamper code/data in the browser, so in theory user may skip any authorization in the browser. He may get access to SPA, so it's is important to have proper authorization also on the backend (API) side. Attacker may have an access to SPA, but it will be useless if API denies his requests.
BTW: You can find hackish advices on the internet how to add authorization to the Keycloak client with custom scripting (e.g. custom scripted mapper, which will test role presence). That is terrible architecture approach - it is solving authorization in the authentication process. It won't be clear why user can't log in - if it is because credentials are wrong or because something requires some role in the authentication process.
You should indeed not create multiple realms, since that is besides the point of SSO systems. Two approaches are possible in your - presumably - OAuth 2.0 based setup:
restrict access at the so-called Resource Server i.e your backend
use a per-SPA "scope" for each SPA that is sent in the authentication request
The first is architecturally sound but perhaps less preferred in some use cases as you seem to indicate. The second approach is something that OAuth 2.0 scopes were designed for. However, due to the nature of SPAs it is considered less secure since easier to spoof.
I was able to restrict users access to application using following approach:
I've created to clients in my default realm (master) i called my clients test_client1 and test_client2 both of them are OIDC clients with confidential access by secret
I've created a role for each of them, i.e. i have role test_client1_login_role for test_client1 and test_client2_login_role for test_client2.
I've created a two users - user1 and user2 and assign them to client 1 and client2 role. But to restrict access to client1 i have to delete default roles:
That did the trick, when i am logging with user2 i see test_client2 and not test_client1 as available application:
But i did n't delete roles from user1 and therefore i could see both clients when i am log in with user1:
Therefore you should use different clients for your applications, assign to each of a client specific role and remove from users default roles and add one releted to specific application.
Are there any decent examples of the following available:
Looking through the WIF SDK, there are examples of using WIF in conjunction with ASP.NET using the WSFederationAuthenticationModule (FAM) to redirect to an ASP.NET site thin skin on top of a Security Token Service (STS) that user uses to authenticate (via supplying a username and password).
If I understand WIF and claims-based access correctly, I would like my application to provide its own login screen where users provide their username and password and let this delegate to an STS for authentication, sending the login details to an endpoint via a security standard (WS-*), and expecting a SAML token to be returned. Ideally, the SessionAuthenticationModule would work as per the examples using FAM in conjunction with SessionAuthenticationModule i.e. be responsible for reconstructing the IClaimsPrincipal from the session security chunked cookie and redirecting to my application login page when the security session expires.
Is what I describe possible using FAM and SessionAuthenticationModule with appropriate web.config settings, or do I need to think about writing a HttpModule myself to handle this? Alternatively, is redirecting to a thin web site STS where users log in the de facto approach in a passive requestor scenario?
An example of WIF + MVC is available in this chapter of the "Claims Identity Guide":
http://msdn.microsoft.com/en-us/library/ff359105.aspx
I do suggest reading the first couple chapters to understand all underlying principles. This blog post covers the specifics of MVC + WIF:
Link
Controlling the login experience is perfectly fine. You should just deploy your own STS (in your domain, with your look & feel, etc). Your apps would simply rely on it for AuthN (that's why a app is usually called a "relying party").
The advantage of the architecture is that authN is delegated to 1 component (the STS) and not spread out throughout many apps. But the other (huge) advantage is that you can enable more sophisticated scenarios very easily. For example you can now federate with other organization's identity providers.
Hope it helps
Eugenio
#RisingStar:
The token (containing the claims) can be optionally encrypted (otherwise they will be in clear text). That's why SSL is always recommended for interactions between the browser and the STS.
Notice that even though they are in clear text, tampering is not possible because the token is digitally signed.
That's an interesting question you've asked. I know that for whatever reason, Microsoft put out this "Windows Identity Foundation" framework without much documentation. I know this because I've been tasked with figuring out how to use it with a new project and integrating it with existing infrastructure. I've been searching the web for months looking for good information.
I've taken a somewhat different angle to solving the problem you describe.
I took an existing log-on application and integrated Microsoft's WIF plumbing into it. By that, I mean that I have an application where a user logs in. The log-on application submits the credentials supplied by the user to another server which returns the users identity (or indicates log-on failure).
Looking at some of Microsoft's examples, I see that they do the following:
Construct a SignInRequestMessage from a querystring (generated by a relying party application), construct a security token service from a custom class, and finally call FederatedSecurityTokenServiceOperations.ProcessSignInresponse with the current httpcontext.response. Unfortunately, I can't really explain it well here; you really need to look at the code samples.
Some of my code is very similar to the code sample. Where you're going to be interested in implementing a lot of your own logic is in the GetOutputClaimsIdentity. This is the function that constructs the claims-identity that describes the logged-in user.
Now, here's what I think you're really interested in knowing. This is what Microsoft doesn't tell you in their documentation, AFAIK.
Once the user logs in, they are redirected back to the relying party application. Regardless of how the log-on application works, the WIF classes will send a response to the user's browser that contains a "hidden" HTML input that contains the token signing certificate and the user's claims. (The claims will be in clear text). At the end of this response is a redirect to your relying-party website. I only know about this action because I captured it with "Fiddler"
Once back at the relying party web site, the WIF classes will handle the response (before any of your code is run). The certificate will be validated. By default, if you've set up your relying party web site with FedUtil.exe (by clicking "Add STS Reference in your relying party application from Visual Studio), Microsoft's class will verify the certificate thumbprint.
Finally, the WIF framework sets cookies in the user's browser (In my experience, the cookie names start out with "FedAuth") that contain the users claims. The cookies are not human readable.
Once that happens, you may optionally perform operations on the user's claims within the relying party website using the ClaimsAuthenticationClass. This is where your code is running again.
I know this is different from what you describe, but I have this setup working. I hope this helps!
ps. Please check out the other questions I've asked about Windows Identity Foundation.
UPDATE: To answer question in comment below:
One thing that I left out is that redirection to the STS log-on application happens by way of a redirect with a query-string containing the URL of the application the user is logging in to. This redirect happens automatically the first time a user tries to access a page that requires authentication. Alternatively, I believe that you could do the redirect manually with the WSFederationAuthentication module.
I've never tried to do this, but if you want to use a log-on page within the application itself, I believe the framework should allow you to use the following:
1) Encapsulate your STS code within a library.
2) Reference the library from your application.
3) Create a log-on page within your application. Make sure that such page does not require authentication.
4) Set the issuer property of the wsFederation element within the Microsoft.IdentityModel section of your web.config to the login page.
What you want to do is an active signin. WIF includes WSTrustChannel(Factory) which allows you to communicate directly with the STS and obtain a security token. If you want your login form to work this way, you can follow the "WSTrustChannel" sample from the WIF 4.0 SDK. Once you have obtained the token, the following code will take that token and call the WIF handler to create a session token and set the appropriate cookie:
public void EstablishAuthSession(GenericXmlSecurityToken genericToken)
{
var handlers = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers;
var token = handlers.ReadToken(new XmlTextReader(
new StringReader(genericToken.TokenXml.OuterXml)));
var identity = handlers.ValidateToken(token).First();
// create session token
var sessionToken = new SessionSecurityToken(
ClaimsPrincipal.CreateFromIdentity(identity));
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
}
Once you have done this, your site ought to behave the same as if passive signing had occurred.
You could use the FederatedPassiveSignIn Control.
Setting your cookie like this:
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
Doens't work for SSO to other domains.
To cookie should be set by the STS not at the RP.