Change Redirect URI of Identity Provider in Keycloak - google-oauth

Whenever we configure Identity Provider in KeyCloak, it automatically sets Redirect URI like
http://keyclaok:8080/auth/realms/{MY_REALM}/broker/google/endpoint
I am implementing multi-tenancy in the project. For every tenant, I will have a separate realm and in turn, have a separate Redirect URI.
For this to work, I need to configure the same URL in "Authorized Redirect URLs" in Google Cloud Platform Console.
To ensure KeyCloak's IDP Redirect URI and Google console's configured Authorized URL match, there are 2 possible options
Dynamically(programmatically) configure Authorized Redirect URL in Google console when new tenant/realm is created. As far as I know, there is no way to dynamically set this authorized redirect URL. So this doesn't look like a viable option.
Configure custom redirect URI in KeyCloak. However, Keycloak doesn't seem to allow changing Redirect URI for Identity Provider.
Any solution, workaround or hints would be appreciated. I feel this is not a unique problem that I am solving. It must be already done and solved by many.

Related

Google oAuth domain issue with our SaaS multitenant webapi

We received this google warning because one of our google projects has multiple domains.
Your project: {app} has multiple unique domains in the redirect URI and origin URLs, many of which have unrelated applications. This is in direct violation of the Google API Services: User Data Policy, which requires that projects accurately represent their identity and intent to Google and to our users when they request access to Google user data.
The first domain is the website of the app {app}.tld
The second domain is the api where OAuth happens {tenant-id}.subdomain.domain.tld
Our violation comes from the domain {tenant-id}.subdomain.domain.tld which is where we host our multi-tenant api.
Is it possible to resolve this issue while still using our multi-tenant api to handle the OAuth dance?
I Guess you are using the {tenant-id}.subdomain.domain.tld as the callback url and adding a unique url for each tenant.
To solve this issue i think you may add a single callback url. Something like callback.domain.tld may work. This endpoint should redirect the original google redirection to {tenant-id}.subdomain.domain.tld with all the query params from google. You can encode the tid in state so that you can decode it to redirect the request properly in the redirection service.
You can also use some services like AWS lambda to act as the middleman and redirect to your final endpoint.
It's okay with that {tenant-id}.subdomain.domain.tld as the callback URL and adding a unique URL for each tenant.

Oauth2 Single-Page Apps Security Considerations

I was reading the site Oauth.com trying to understand how implement security in a Single-Page App when i found this statement:
"The only way the authorization code grant with no client secret can be secure is by using the “state” parameter and restricting the redirect URL to trusted clients. Since the secret is not used, there is no way to verify the identity of the client other than by using a registered redirect URL."
If i undestanf correctly, he says that i can verify the identity of my SPA using a registered redirect URL.
Question 1: If I redirect the authorization code to a url (web server), how can i get it (or the access token, or the protected resources) back in my SPA that is running in a browser ?
Question 2: what kind of check can be done in this registered url to verify my SPA identity?
Q1. You receive an authorization code as the login response in a query parameter. Then swap it for an access token. Then call the API with the access token.
Q2. Use HTTPS to prove that you own the redirect URI. Use PKCE to create a one time use secret at runtime.
RESOURCES OF MINE
Maybe these resources will give you a clearer idea - feel free to post any follow up questions ..
SPA Message Workflow
SPA Tutorial + Code Sample
The authorization code is included in the URL as the code query parameter, so you can access it from the SPA (e.g. with window.location.search). It will depend on the OAuth2 provider whether you will be able to exchange that code for an access token from your SPA.
If you use a web server to do the code->token exchange, you'll need to put the tokens in a database that your SPA has access to.
The quote from OAuth.com mentions two security measures, the state parameter, and registered redirect URLs. These prevent two separate attacks:
The state parameter prevents an attacker from using your URL with a stolen authorization code to retrieve an access token for a victim user and associate it with the attacker's account. By verifying that the state parameter in the URL is the same state that you provided to the authorization server, you can be sure that the redirect was generated by the authorization server, and not an attacker.
The registered redirect URL prevents an attacker from using your client ID, but sending users to an authorization URL that, after authorization, will redirect them to the attacker's web server instead of yours, allowing the attacker to retrieve the user's access token. With a registered redirect URL, the authorization server will only redirect to the pre-registered redirect URLs, which you control, and will not redirect to an attacker's web server.
An alternative to implementing this flow yourself, particularly for a SPA, which has limitations in what OAuth grant types are available to it, is to use a managed OAuth service. Xkit, which I work on, is designed to work with SPAs and moves all of the OAuth flow (including security considerations) out of your code.

Set or Get cookies for different domains (not on a subdomain)

I have a file server, or more of a personal CDN, which I want to protect against unauthorized access. Assuming that this personal CDN is accessible through https://www.personalCDN.com, and the requests to this file server come from the following domains:
1- https://www.Application1.com
2- https://www.Application2.com
3- https://www.WebSite1.com
In case I want the authentication layer to be cookie-based in a way that, when the user logs into any of these three domains, a cookie for https://www.personalCDN.com would be added to browser, and then it would be used by the main file server, how can I handle this situation in ASP.Net Core 2.x? I've read that, writing cookies for other domains is not allowed in JavaScript, does it apply to Asp.Net as well?
It's not a JavaScript limitation, it's a functional limitation, in general. Cookies are domain-bound. They can only be written on the domain where the response originates. Likewise, a cookie can only be read by the domain that set it. This is for security reasons, and there's absolute no way around it.
What you would need is a SSO (single sign on) solution, which is not a trivial thing to set up. You essentially have one server that acts as the auth gateway. Your other websites redirect users to that server to login, the user logs in on the auth gateway, where a cookie is set for that domain (i.e. auth.domain.com). This keeps the user logged in on the gateway. Then, the user is redirected back to the originating site, with a token. That token is then validated via an API backchannel to the auth gateway. If the token is valid, then the originating website "logs in" the user, setting a cookie for its domain, as well. Rinse and repeat with all your other websites.
For something like your CDN, you would likely need a site hosted on a subdomain of that same domain (since the CDN itself wouldn't be able to coordinate the auth process). That site, then, would set a wildcard domain cookie, which would be usable by the CDN as well.
You can either set up all this infrastructure yourself (not recommended), or there's third-party libraries like IdentityServer that could ease implementation. Additionally, you can outsource the whole shebang to a third-party provider like Auth0, where it essentially becomes your gateway.

Single sign-on flow using JWT for cross domain authentication

There is a lot of information on the web about using JWT (Json Web Token) for authentication. But I still didn't find a clear explanation of what the flow should be when using JWT tokens for a single sign-on solution in a multiple domains environment.
I work for a company which has a lot of sites on different hosts. Let's use example1.com and example2.com. We need a single sign-on solution, which means if a user authenticates on example1.com, we want him to also be authenticated on example2.com, automatically.
Using the OpenId Connect flow, I understand that the user who wants to authenticate on example1.com will first be redirected to the authentication server (or OP : "OpenId Provider"). The user authenticates on that server which then redirects him back to the original example1.com site with a signed JWT token. (I understand there is another flow which returns an intermediate token that itself can be exchanged for the real JWT token later on, but I don't think this is required for us)...
So now the user is back on example1.com and is authenticated! He can make requests, passing the JWT token in a Authentication header and the server is able to verify the signed JWT and therefore is able to identify the user. Nice!
First question :
How should the JWT token be stored on the client? There is, again, a lot of information about this, and people seem to agree that using Web Storage is the way to go rather than good old cookies. We want the JWT to be persistent between browser restarts so let's use Local Storage, not Session Storage...
Now the user can restart his browser and he will still be authenticated on example1.com, as long as the JWT token is not expired!
Also, if example1.com needs to make an Ajax request to another of our domains, I understand configuring CORS would allow that. But our main use case is not cross-domain requests, it's having a single sign-on solution!
Therefore, the main question :
Now, what should the flow be, if the user goes to example2.com and we want him to be authenticated, using the JWT token he already has? Local Storage doesn't seem to allow cross-domain access so at this point the browser can't read the JWT token to make requests to example2.com!
Should :
The user be redirected to the authentication server again? When the user authenticated for example1.com, the
authentication server may have set a cookie on the user so this new authentication request for example2.com could use that cookie to see that the user is already authenticated and immediately redirects him back to
example2.com with the same JWT token?
Or can the browser, on example2.com, access the JWT token without having to go to the authentication server again? I see there are cross-storage solutions, but are those widely used? Are they the suggested solution to a cross domain SSO environment?
We don't want anything fancy, we would be happy with the mostly used solution!
Redirecting the user to the central authentication service when the user is not logged in to request credentials and issue a new authentication token is the common scenario in Single Sign On systems using well-known protocols like oauth2 or OpenId Connect
However when this schema is used across domains the main drawback is that the user is going to be redirected and authenticated each time he navigates to other domain due to same-origin policy: the access token can not be shared between domains (example2.com can not access data of example1.com), so the target domain will treat user as unauthenticated, redirecting him to the central SSO service.
To prevent the authentication service from re-requesting credentials, it is common to have a session cookie (not an access token), but there is a tecnique to share data across domains using browser localStorage/cookies and a iframe pointing to an intermediate domain sso.example.com
To authenticate the user in example1.com, redirect him to the authentication server in sso.example.com, issue a JWT after authenticating and store it in the localStorage of this domain. After this, redirect user to the origin domain example1.com
Create an iframe in example2.com pointing to sso.example.com. The iframe in sso.example.com reads the JWT token and sends a message to the parent page
The parent page receives the message and gets the attached token continuing with the SSO flow
There is no problem with same-origin policy because sso.example.com has access to its localStorage and the communication between iframe and the parent page is allowed if origin and target domains recognize each other (see http://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage)
To simplify development, we have released recently a cross domain SSO with JWT at https://github.com/Aralink/ssojwt
This method is perfectly compatible with SSO flows. It is just a way to share the authentication token without redirections and avoid unnecessary log-ins when the domains are federated
The user should be redirected to the authentication server again and get a new token (JWT), one that is specifically targeted for example2.com. This is how OpenID Connect and any other cross-domain federated SSO protocol works.
Not sure if this answers you question, but if your main goal is single sign-on, I think a simple reverse proxy would solve your problem (at least the cross-domain storage one).
So
example1.com
example2.com
would become something like
example.com/example1
example.com/example2
(And from a user side, this is usually cleaner)
If that is not an option, you might have to set up so that when a user authenticates in 1 domain, it uses AJAX/hidden iframes to create an authentication with the other domains as well (sending a 1 time token via url if you must).
and if THAT'S not an option, you might have to resort to username+pin, as browsers are getting stricter about cross-domain interaction.

Can I redirect with access_token without an "allow" screen?

My API should support "official" apps as well as third-party apps. For official apps I don't want the user to explicitly grant access (after login).
I'm I right, that I can skip the "allow" screen as long as I restrict the redirect_uri to something like http://official.service.tdl? Or does this has security implication I didn't think of?
For official apps, assuming they are confidential clients, you can use client credentials grant. It sounds like the official app and your API will have a pre-established relationship, and your organization also controls the access server. In the client credentials grant the client can just talk to the access server directly and gets a token to use with your API.
If you want to bypass user authorization when the redirect uri is from your company, then you should look at Authorization Code Redirection URI Manipulation, in particular
In order to prevent such an attack, the authorization server MUST
ensure that the redirection URI used to obtain the authorization code
is identical to the redirection URI provided when exchanging the
authorization code for an access token. The authorization server
MUST require public clients and SHOULD require confidential clients
to register their redirection URIs. If a redirection URI is provided
in the request, the authorization server MUST validate it against the
registered value.
and you would need to make sure you don't have any open redirects that match your redirect URI.
Or you can just let users authorize access once and use refresh tokens so they don't need to re-authorize access.
Since the redirect URI is coming from the client browser, you cannot guarantee that someone won't setup their browser to change the URI to make it look like it's coming from your official URI. The question then becomes, what security issue would you run into if a third party app bypasses the explicit access.