Keycloak - Multi/2FA Factor - OTP - QR Code - Custom Login Screen - Rest API - authentication

I have my own Login page where user enters username/password.
This username/password are used to login through Keycloak Rest API.
http://localhost:8080/auth/realms/Demo/protocol/openid-connect/token
input - {username,password,grant_type,client_secret,client_id}
And in response i get access token.
Now i wish to enable Authenticator (Google Authenticator). I have enabled it from backend. Now if user wishes to login thorugh my application, my login page i need to get below details.
1.) Somehow i need to include QR Code that appears on keycloak login page post username/password validation to show on my login screen for the first time login once user enter username/password. So do we have any API which return Keycloak QR code image in response.
2.) Subsequent login i will have OTP field, so need a REST api to pass OTP along with username/password.
Please help with REST API if keycloak has any. Integrating through Javascript.
Similar flow as described in use case 1 here
Just want to use keycloak as a database, doing all operation for me, input will be my screen. I do want redirection of URL's while login in and yet should be standalone deployed.

I've managed to implement this through the rest API of Keycloak. To realize this, you need to extend Keycloak yourself with a SPI. To do this create your own Java project and extend org.keycloak.services.resource.RealmResourceProvider and org.keycloak.services.resource.RealmResourceProviderFactory. You can find more information in the official docs (https://www.keycloak.org/docs/latest/server_development/#_extensions), github examples and other stack overflow posts how to do this.
Once you got this up and running you can implement it like this:
#GET
#Path("your-end-point-to-fetch-the-qr")
#Produces({MediaType.APPLICATION_JSON})
public YourDtoWithSecretAndQr get2FASetup(#PathParam("username") final String username) {
final RealmModel realm = this.session.getContext().getRealm();
final UserModel user = this.session.users().getUserByUsername(username, realm);
final String totpSecret = HmacOTP.generateSecret(20);
final String totpSecretQrCode = TotpUtils.qrCode(totpSecret, realm, user);
return new YourDtoWithSecretAndQr(totpSecret, totpSecretQrCode);
}
#POST
#Path("your-end-point-to-setup-2fa")
#Consumes("application/json")
public void setup2FA(#PathParam("username") final String username, final YourDtoWithData dto) {
final RealmModel realm = this.session.getContext().getRealm();
final UserModel user = this.session.users().getUserByUsername(username, realm);
final OTPCredentialModel otpCredentialModel = OTPCredentialModel.createFromPolicy(realm, dto.getSecret(), dto.getDeviceName());
CredentialHelper.createOTPCredential(this.session, realm, user, dto.getInitialCode(), otpCredentialModel);
}
The secret received with the GET must be send back with the POST. The initial code is the one from your 2FA app (e.g. Google Authenticator). The QR code is a string which can be displayed in an img with src 'data:image/png;base64,' + qrCodeString;

I know this is an old question, but I've recently been looking at something similar, and so thought it would be potentially valuable to share what I have found for others who may be looking into this and wondered what the possibilities are.
TL;DR
You can only really use the existing Keycloak actions to do this or embed the user account management page found at https://{keycloak server URL}/auth/realms/{realm name}/account in an iframe. That's it, I'm afraid. In my opinion it is currently best to just assign actions directly to accounts or use the Credential Reset emails to assign actions; both of these can be done via the Admin API if desired:
Send Credential Reset email containing assigned actions:
https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_executeactionsemail
Set actions directly on the account (include the actions in the requiredActions portion of the user JSON that you send in the body to the endpoint):
https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_updateuser
Background is that as part of a project that I have been working on we wanted to see if we could have an integrated way for users to set up their initial password and OTP device when a new account has been created for them, since the default method of sending them an email from Keycloak using the "Credential Reset" functionality has the limitations that a) it doesn't provide a link to the application itself unless you override the theme, and if you have multiple instances of the application for different users you have no way of knowing which instance to provide the link for, so may have to end up including a list of them, and b) it often doesn't feel truly native to the application, even with changes to the theme. If you're sensible though, I'd suggest you stop and just use this functionality - please see the TL;DR section above for details.
So, in short there is NO API endpoint for receiving a QR code to set up an OTP device. There are two places, however, where the QR code can be retrieved from - the OTP device setup screen when you log in as a user who has had a "Configure OTP" action assigned to their account, and the user's own account management screen.
The first option of the Configure OTP action screen is a non-starter. It only shows up when you log in, and so by definition the user has to log in to Keycloak via the Keycloak login page in order to trigger the page to be displayed. At this point you're already on a Keycloak page instead of one of your app's pages, and so unless you can get very creative with changes to these Keycloak pages via a custom theme, tapping into this page isn't really an option.
The second option is more interesting, but far from ideal. Every user who has logged in has access to an account management page that can be found at https://{keycloak server URL}/auth/realms/{realm name}/account. This page allows you to do things like change your name, password, etc. and it also allows you to add an OTP device if you don't already have one, or delete any existing OTP devices associated with your account. This OTP device tab of the account management page can be reached directly via https://{keycloak server URL}/auth/realms/{realm name}/account/totp.
As I mentioned, there isn't an API that you can access to view the QR code that shows up on this page. The only way it is accessible is via the GET request to https://{keycloak server URL}/auth/realms/{realm name}/account/totp, which returns the HTML for the page I've already mentioned. Okay great, so can we scrape the QR code programmatically and then put it in our own page on our application? Err, no, not really. You see, whilst a lot of the Keycloak API endpoints rightly allow you to send a bearer token (e.g. access token) in the authorization header to access and endpoint, this page will not accept a bearer token as a means of authentication/authorization. Instead it uses a session cookie that is locked down to the Keycloak URL. This cookie is set when you log in to your application via the Keycloak login page, and so is available to this account management page when you navigate to it, having already logged in, and since the account management page uses the same server and domain name as the original Keycloak login page, it has access to the cookie and can let you in. This cookie cannot be sent by your application to e.g. your own REST API to then programmatically call the account management page and scrape the QR code, because your application doesn't have access to it for good security reasons. This might be something you can change in Keycloak somewhere, but if there is I would strongly recommend against changing it.
So if we can't scrape the page from our own server, can we do something on the front-end? Well, as mentioned, your application doesn't have access to the session cookie but if you make a request (e.g. using fetch or axios) in your front-end JavaScript to the account management page then that request will send the cookie along with it, so that could work right? Umm, well actually you will get hit with an error message in this scenario due to CORS. CORS is Cross-Origin-Resource-Sharing and in order to allow the Keycloak page to be accessed then you would have to open up the settings on the server to allow it to be accessed from your website's address. I've seen some articles that look at how you can open up your CORS settings on Keycloak if you wish but I'd be very nervous about doing this. I don't know enough about the internals of Keycloak and how it operates to comment on how much of a security risk this is, but I certainly wouldn't recommend it. There some information here (Keycloak angular No 'Access-Control-Allow-Origin' header is present) on changing the "Web Origins" setting of your application's Keycloak client, but this opens up your application to some serious potential abuse. There is also the MAJOR issue that even if you scraped the QR code, the device isn't actually added to the user's account (even though it appears in the authenticator app) until you enter a code into the page that the QR code is on and click Save. Since there isn't an API endpoint that you can use to complete this operation, I therefore don't think that this option is viable either. I've tried out whether or not you can use the token retrieval endpoint at https://{keycloak server URL}/auth/realms/{realm name}/protocol/openid-connect/token to see if making a request with your username/password/otp code will somehow "register" your device and complete the process, but although you can get a token this way, and it doesn't complain about the otp code, it doesn't actually take any notice of the code because as far as it's concerned the user's account doesn't have a device registered with it. So we have to use the form on the account management page in order to complete this registration process.
So the final way of possibly doing this is.... an iframe. Sorry, yeah it's rubbish but that's all your left with. You can have the iframe point at your account management page, and because the user is logged in then they will be able to see the contents from your application's page. You can use relative positioning, fixed width and height and remove scroll bars to ensure that you ONLY show the QR code and the fields for the one time code, device name, and the Save/Cancel buttons. This, sadly, seems to be the only option at the moment, and due to how nasty and unreliable iframes can be in general - they certainly don't feel native to your application, and you'll need to override your Keycloak theme to get the page in question to look more like your app - I'd recommend steering clear of this and using the standard approach of using Keycloak actions and the Admin API instead.
If you've made it this far, congratulations, you win at Stack Overflow :-)

Related

Is there a way to have a 'Google Sign In' button for google accounts that are not signed up with Google Plus?

I'm working on an internal website for the company I work for. The website will be only available to company staff. We use Google Apps for Business, so we would like authentication to be done using our google accounts.
I've gone through 'google sign in' samples from here: https://developers.google.com/+/
It works, but the problem we run into is that it requires the user to sign up to Google+. This is a speed bump we would prefer not to have.
Are there any ways around this? Thanks.
It shouldn't be too hard to roll your own sign in using the lower levels of Oauth, eg 'email' scope. It's hard to give a more specific answer because it depends on your architecture (eg. are you predominantly server-side or client-side) and what kind of session do you want to create by the sign in process. For example, if you are client/REST based, you probably don't want any session at all as REST encourages statelessness. On the other hand, if you are more web based, serving static pages, you will want a session.
In simple terms, you will be doing something that generates an access token, and then processing that access token to determine the email address (or Google ID) of the person who created it. You will then establish some sort of session (eg. using session cookies) that identifies future requests from that user.
Feel free to add some more detail to your architecture and I'll try to finesse the answer.
For simple http servlet sessions, it will be something like.
User requests a protected page
servlet detects that there is no session and/or session has no authenticated user
servlet redirects to an Oauth page to request an access code. something like
https://accounts.google.com/o/oauth2/auth?redirect_uri=xxx&response_type=code&client_id=zz&approval_prompt=auto&scope=email
NB research the exact URL, don't rely on this to be exact
If the user isn't logged on, he'll be prompted; if he has multiple logins, he'll be prompted; if he hasn't yet granted email access, he'll be prompted. If none of these conditions are met (the normal case) he won't see anything.
Browser will redirect to the redirect_uri, carrying an access token (or an auth code if this is the first time the user has used the app)
Post the token to the Google userinfo endpoint, and you will receive a decode containing the email address
Store the email into a session object (or retrieve your own user object and store that)
redirect back to the originally requested page. You can use the OAuth state parameter to pass that around
et voila. all future page requests from that user will be within a session containing some user identification.
NB This is just an outline and I may even have missed a step. You will still need to do your own OAuth research.
Apparently not:
(..) if a Google user who has not upgraded to a Google+ account clicks
on the Sign in with Google+ button, the same consent dialog that opens
will take the user into an account upgrade flow.
Weirdly the docs for OAuth2 states:
Google+ Sign-In works for all users with a Google account, whether or
not they have upgraded to Google+.

Facebook Login without JSSDK, how to get token if already authorized previously

So I am updating an older desktop app (written in VB, .net 4.0) with facebook integration and followed the guide found here, and have been able to successfully get a token (by parsing the uri of the embedded webview if it contains "token="). Now my problem is if I try to login with a facebook account that has already approved the app in a prior session, the webview just gets redirected to https://www.facebook.com/connect/login_success.html without any token information.
Do I HAVE to log all of the tokens I generate manually (ie on successful token generation, I can call their profile info, use their FB ID as key and save the token)? Even if I do, since the email and password is input directly into the facebook login window, how do I check if the user already has a token?
Thanks in advance
The access token can change any time, you need to get it everytime. After getting the token, I immediately get the user information https://graph.facebook.com/me?access_token=??? and use that ID to find their database information.
I couldn't quickly find facebook information but on google's oauth information it says "The access token is also associated with a limited scope that define the kind of data the your client application has access to (for example "Manage your tasks"). An important goal for OAuth 2.0 is to provide secure and convenient access to the protected data, while minimizing the potential impact if an access token is stolen."
https://code.google.com/p/google-api-php-client/wiki/OAuth2
Ok so I finally figured it out myself. My mistake was apparently requesting the access_token directly (ie https://www.facebook.com/dialog/oauth?response_type=token...) to try and save time.
I fixed it by making a request for a 'code' instead (ie https://www.facebook.com/dialog/oauth?response_type=code), which I then use to make a second request to retrieve an access token as documented here: https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/, "Exchanging code for an access token" section a bit lower on the page.
Hope this helps someone in the future, this was very frustrating on my part.
Regards,
Prince

Replace "via Graph API Explorer" label by my application name

I am quite new in Facebook application development. I was playing the all day with "how to post a message on the wall of a page?". I finally succeeded but each message got "via Graph API Explorer". I tried to find how to change it to my application name without success. I tried to see if I could force the value of application in the api command but it did not take it into account. Maybe I miss something :( If someone can help, that would be great!
I am still quite confused. Let me try to explain what I want to do: I would like to automatically publish on a page (as the page) some event that are defined on a website (in a kind of agenda). What I miss, I think, is how everything is working together on Facebook side:
1. the login process: as the application will run in a cron, this should not display a login dialog box.
2. the access token: application or page one?
3. the permissions: from my understanding, I need manage_pages (and publish_stream) but not clear how this should be set.
Thx for any clarification and maybe a clear example :o)
You need the user to authorise your own App using one of the Login flows and grant you one of the publishing Permissions -
If it says 'via Graph API Explorer' on the posts your app makes you're using the access token you retrieved when you were testing the API using the Graph API Explorer tool, not one produced by your own app
OK I think I have finally found the way to do it. I needed a page access code and not an application access code. The token must be generated outside the application as a long live one.
Get a code using:
https://www.facebook.com/dialog/oauth?client_id={app_id}&redirect_uri={my_url}&scope=manage_pages,publish_stream
app_id is your application ID
my_url is your application URL
scope is the permission you want to be granted
In the redirected URL, you will have a code parameter. Copy it.
Generate the user access code using:
https://graph.facebook.com/oauth/access_token?client_id={app_id}&redirect_uri={my_url}&client_secret={app_secret}&code={code}
app_secret is your application secret key
code is the code from step 1
You will get as output the user access token. This one is a short live one.
convert the short live to a long live user access token using:
https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id={app_id}&client_secret={app_secret}&fb_exchange_token={short live access token}
Replace the "short live access token" by the one you got on step 2
You will get as output the infinite user access token.
Get the page access token (this one will be infinite access token as
the user access token is now an infinite access token too):
https://graph.facebook.com/me/accounts?access_token={infinite user access token}
Replace the "infinite user access token" with the value you got on step 3.
This command will list all the pages you administer. The output contains the page access token you need in field "access_token". You can so use this token in any API command in your application.
The best of the best is to do all those steps via a server side program (PHP for me) as the application secret key should remain "secret".

GITkit "Account Chooser" Questions

Has anyone successfully implemented the Google Identity Toolkit, an implementation of an Account Chooser. I followed the initial steps here, but I still have a few questions, as I don't quite know how to handle the entire data flow. I'm using Clojure / Compojure in the back-end:
http://havethunk.wordpress.com/2011/08/10/google-identity-toolkit-asp-net-mvc3/
http://code.google.com/apis/identitytoolkit/v1/acguide.html
A) don't quite understand how ID Provider authentication, fits into my data model
when implementing the callbackURL, what data should I expect, and
how's that session state managed by GITkit (and all Account Choosers)
B) Is there a way to set this up the 'callbackURL' for development.
the identity provider would need a URL that it can redirect back to
C) How can the GITkit / Account Chooser workflow let my users register an account that's native to my app?
Thanks in advance
The questions aren't entirely clear, but I've done an implementation of GITkit in ruby and can give you some pointers.
A) The callback URL is what handles the assertion from the identity providers. Rightnow GITKit only does OpenID, so the URL will contain an OpenID response either in the query parameters or as the POST body. You'll need to do a few things:
1) Call verifyAssertion in the gitkit API and pass the params/post body. This will return a JSON response that contains the user details (assuming assertion is valid). There are some other checks you should do as well
2) Decide what to do with the assertion. If it is an existing user, most likely you'll just establish a session and save the user ID. If it's a new user, you can either create a new account and start a session immediately, or defer that and redirect them to a signup page.
3) Render HTML/JS to notify the widget. There are different status codes and data you can return that changes the flow.
GITKit itself doesn't really manage session state, that's up to your app. Some of the reference implementations have code to help, but it's not part of the API. The widget does have some state that you can control with JS (add account, show as logged in, etc) and uses local storage in the browser.
The docs give some details and example code for how this should be implemented.
B) Of course. The URL is just configured in the javascript widget when you call setConfig() It can be set to localhost or any staging server for development. So long as your browser can reach it you're OK.
C) By "native", I assume you mean where they're signing up with just a username/password instead of using an IDP. If so, the user just has to enter their email address when logging in. If that email address matches a known IDP it'll attempt to authenticate with OpenID, otherwise if it's a new user it'll redirect to whatever signup page you configured in the widget. That signup page would just ask the user to create a password like you normally would. You should also return whether or not accounts are 'legacy' (password) accounts in the userStatus checks.
Hope that helps.
For anyone's future reference. I was able to resolve the issue. You can follow this thread of how's it's done in Clojure.
I got it working with Ring/Compojure, and another fellow showed me his solution in Webnoir.
HTH

GWT: Authentication for some part of application using GWT login page

My application has some features that are accessible to all users, and some other features to which access should be restricted to authenticated users only. All these restricted features exists within some set of GWT Places, thus, all Places available in application can be divided into two groups: "accessible for all", and "restricted". In my opinion, places with restricted access, could implement some interface (let's say it would be RestrictedAccess), and if user proceeds to one of them, and it has not been authenticated yet, it will be redirected to the login screen - it's more OO-approach than applying filters basis on URL.
What I'm trying to achieve is:
Information about if user has been
authenticated or not should be
stored on server (it's not something
that could be stored in a cookie...)
Login page is a standard GWT place+view+activity (!)
User name & password validation is done on the server side.
So far, I've introduced RestrictedAccess interface, which is implemented by some set of places. My FilteredActivityMapper.Filter implementation, which is passed to the FilteredActivityMapper wrapping application activity mapper has the following logic:
Place filter(Place place) {
if (place instanceof RestrictedAccess && !userHasBeenAuthenticated()) {
return new LoginPlace();
}
// return the original place - user has been already authenticated or
// place is accesible for all users
return place;
}
private boolean userHasBeenAuthenticated() {
// remote call - how to do ???
}
The problem is with userHasBeenAuthenticated() method (user should not be redirected to the LoginPlace, if it has been already authenticated). If I want to store this information on the server-side, I have to do GWT RPC/request factory call here, but both are asynchronous, so I cannot work on its result in the filter method.
I know that I can use web.xml filters or some external framework (e.g. spring security), but none of this approach allows me to have login page as a standard GWT - based form, or indicating in the more OO way that access to some place should be restricted.
Thanks in advance for any hints
EDIT: I've started to wondering if places filtering (restricted/not restricted) should take place on the client side at all. If, as it was suggested, there is a possibility to hack code indicating if user has been authenticated or not, there is also possibility to hack places filtering code, so that it will be possible to access restricted places without signing in.
Piotrek,
I think there is a security issue with calling userHasBeenAuthenticated() - it would be possible to hack the client side code to return true every time this function is called.
The solution I've implemented is to simply return SC_UNAUTHORIZED if an unauthenticated user attempts to access any remote service. I've overridden the RequestFactory onResponseReceived function which redirects to a login page if the response is SC_UNAUTHORIZED. Idea taken from:
http://code.google.com/p/google-web-toolkit/source/browse/trunk/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/GaeAuthRequestTransport.java
This works for our situation where the Activities and Places are all data-centric - each place change retrieves data from the server. If a user isn't authenticated they simply don't get the data and get redirected to a login page.
I realize your situation is slightly different in that some places are accessible to everyone, in which case you could configure only the restricted services to return SC_UNAUTHORIZED.
I have a similar application with the same requirements. As yet I have not got round to to the implementation but I was thinking along the same lines.
What I was planning on doing is storing the authentication state client side in an AuthenticationManager class. When the app starts I was going to request the login info from the server (I was thinking of running on app engine so I would get the authentication state and also get the open id login/logout URLs) and store this in the AuthenticationManager. Acegi/Spring Security works in a simlar way so this info is available server side if you use those too.
When the user logs in/out they will be redirected by the server and the new state will be retrieved. This should keep the client authentication state in line with the server. Each RPC request on the server has to be checked for authentication too. I was using the gwt-dispacth library and this has some rudimentary authentication checking and cross site script protection in in too (although I think latest GWT has this for generic RPC).
One issue is session timeouts. Again the gwt-dispath library has some code that detects this and returns session expired exceptions to the client which can be intercepted and the auth manager updated.
Hope that makes some sense.