how to implement passwordless authentication in identityserver3 - identityserver3

I'm looking for the correct way to customize or extend identityserver3 to implement passwordless authentication.
the user scenario is
user uses browser to go to website
website detects non-authenticated
user and redirects to idsrv3 instance
idsrv3 asks for email address (or uses a cookie stored one) and if email address matches a known user, sends an email with a link (e.g. like the registration or password reset link)
user clicks on link
idsrv3 instance checks if token in link matches a known user and is not expired
idsrv3 issues token
idsrv3 redirects to original url
after some reading, I think the correct way to customize idsrv3 would be to
create a custom viewservice
checks the emailaddress
generates and stores a token in the user account record
sends an email with a link consisting of a custom grant, the token and a returnurl
create a custom grant and corresponding validator
checks the incoming token and if valid returns a positive validation result linked to a user account
the normal idsrv3 token issuing flow takes over to issue a token and redirect to the returnurl
In my experience, idsrv3 is great, but it can be time-consuming to get things right when customizing and I would like to be sure that I don't hit a brick wall somewhere near the end of the solution.

Related

Is there a possibility to automatically login by clicking on verify email link triggered by /send-verify-email

If Keycloak's self-registration form is used, once submitted, it will send an email verification link to that newly registered user.
Once the user clicks the link, his email will be verified, and it will redirect him to the redirectUri from his client and provide the authorization code, which is perfect.
The problem arises when I don't want to use Keycloak's registration form, instead I have a custom registration mobile form, which sends the user data. Those data is then sent to the Keycloak by Admin REST create user API.
Once the user is created, by triggering /send-verify-email or /execute-actions-email APIs none of them are returning authorization code in the end. Yes, they do redirect to the redirectUri which you specified, but I need authorization code as well, in order to be logged in automatically upon clicking on the link.

RESTFul API Design Suggestion for forgot password flow

I'm working on designing Restful API for forgot password flow.
The flow to be used in web application is below:
1) User enters user id
2) Application validates user id and sends verification token to user's email address
3) User will be asked for validation code and new password
4) User will enter validation code and new password
5) Application validates token and updates password.
I would appreciate your suggestions on how this flow can be translated to Restful API's.
Thank you,
Raj
This is a very standard design, I think you can find lots of resources, or even reset your own password on some website and see how that works.
The basics:
The client will make a POST request when sending the user id. Note that it's a good idea to NOT say if the user id / email is valid (to avoid brute force checking of user id / email existing). Also, make it so that you can't request another password reset until the previous token has expired (to prevent DoS attacks).
As you said, the server will check the associated email and eventually send the token.
There is no actual need for the user to enter the validation token. Just like most sites do, you can embed that in the link that you send in the email. Remember to set a reasonable expire time for the token (maybe one hour?). Here you should already check the token validity.
The link with the token will be a GET request to a specific page where the user is automatically authenticated and can enter the new password (again: remember the expiry time on the token).
The user will enter the new password with a POST request, you check the token validity again, and if everything matches, you update the password.
In terms of POST and GET calls that can be something like:
POST https://www.yoursite.com/resetpassword, with the user id / email in the body. Don't put the user id / email as a query or path parameter, especially if you have ads / banners on the page, because they might be able to get that data.
GET https://www.yoursite.com/password?token=dhs3541hpk43hokdsau9ef where the token is associated with the user id / email resetting the password (you should have this in a database). Here the token is validated for the first time and the user can enter the new password in a form. Note that this can be the same form that you use when the user is changing the password, just without the Old password field. Note that you should NOT have banners / ads on this page, ever. That would be a serious security risk.
POST https://www.yoursite.com/password?token=dhs3541hpk43hokdsau9ef with the new password in the body. Again, don't put banners / ads on this page. The server will check the token again and if it matches, update the password. Then the server will mark the token as invalid / expired.

keycloak - realm resolution based on username (email address)

I'm working on a multi tenant project where usernames are actually their email addresses and the domain of the email serves as a tenant identifier.
Now in keycloak I'll have different realms per tenant, but I want to have a single login page for all tenants and the actual realm that will do the authentication to be somehow resolved by the username (email address).
How do I go about doing that?
I found a thread on the mailing list (that I cant find now...) that discussed the same problem. It was something along the lines of - create a main realm that will "proxy" to the others, but I'm not quite sure how to do that.
I think Michał Łazowik's answer is on the right track, but for Single-Sign-On to work, it needs to be extended a little.
Keep in mind that because of KEYCLOAK-4593 if we have > 100 realms we may have to have multiple Keycloak servers also.
We'll need:
A separate HTTP server specifically for this purpose, auth-redirector.example.com.
An algorithm to determine the Keycloak server and realm from a username (email address).
Here would be the entire OAuth2 Authorization Code Flow:
An application discovers the user wants to log in. Before multiple realms, the realm's name would be a constant, so the application would redirect to:
https://keycloak.example.com/auth/realms/realname/protocol/openid-connect/auth?$get_params
Instead, it redirects to
https://auth-redirector.example.com/?$get_params
auth-redirector determines if it itself has a valid access token for this session, perhaps having to refresh the access token first from the Keycloak server that issued it (the user could have logged out and is trying to login as a different user that is served by a different realm).
If it has an valid access token we can determine the Keycloak server and realm from the username or email address in the access token and redirect to:
https://$keycloak_server/auth/$realm/realname/protocol/openid-connect/auth?$get_params
from here, the OAuth2 Authorization Code Flow proceeds as usual.
Else if it doesn't have a a valid access token, the auth-redirector stores the original app's $get_params as session data. It presents a form to the user asking for a username. When the user submits that, we can determine the Keycloak server and realm to use and then auth-redirector itself logs in to the Keycloak server using its own $get_params. Once the auth-redirector gets a call-back, it retrieves the access+refresh token from the Keycloak server and stores them in session data. It then, finally, redirects back to that same keycloak server and realm with the callers original $get_params (from session data). And the OAuth2 Authorization Code Flow proceeds as usual.
This is definitely a hack! But I think it could work. I'd love to try it out some day, time permitting.
Other hacks/solutions are needed for other OAuth2 flows...
The idea from the mailing list is to write a service (let's say auth-redirector.example.com) that has a single input field for email, finds realm based on domain and redirects to that realm's keycloak endpoint (e.g. auth.example.com/auth/realms/realm-name/etc…) while keeping all GET params.
You can find examples of direct login/registration URLs here: https://lists.jboss.org/pipermail/keycloak-user/2016-July/007045.html
One usability problem is that users would have to provide their email twice, I have not yet found a way to pass the username via the login URL.

Oauth2: authorize access based on unguessable url in email

Our application uses oauth2 & openid connect for auth&auth. It's built using an angular client that calls a REST API. I would like to know how to authorize access to the API, based on the possession of an unguessable url.
I'll explain this a little more. In the application, a user can invite another user. When this happens, an email is sent to the second user. When user 2 clicks a link in the email, he is sent to a webpage with details about the invitation.
Only user 2 should be allowed to see the invitation page. I was planning to solve this by using an 'unguessable url' in the email. Upon visiting the url, the user must somehow be authorized to fetch the invitation details from the API.
The question: how do I authorize a user, based on knowing the unguessable url? How do I assign a claim when the page is loaded, and how do I verify this claim in the API call that follows? The only solution I see, is to set a cookie containing a token. But this is not in line with our existing auth mechanism. I prefer not writing my own token validation code, and let the Identity Provider handle this.
Additional info: user 2 may or may not have an account in the system, and he may or may not be logged in. Neither should prevent the user from seeing the invitation details. In other words: a totally unknown user should be able to see the page. The knowledge of the url should be the only requirement.
Any solution to this problem? Or am I handling it all wrong?
After asking around, the general consensus is to NOT let the external auth mechanism take care of this, but to validate the link ourselves.
The solution is to turn the unguessable part of the url (the 'link id') in some kind of token, which can be validated upon calling the API. This is done by the API itself, not by the Identity Server.
Applied to the invitation issue: when an invitation is created, store the link id together with some info, i.e. what kind of access it allows (invitation access) and the id of the invitation. When the user calls the API to get the invitation, pass the link id for validation. Match the invitation id with the invitation id stored in the link, and if it doesn't, throw an error.

Rest APIs to support forgot password functionality which span to multiple steps

I have to develop a Rest API for forgot password functionality which span in three different workflow i.e.
First I need to verify user input for name, email and phone. If all are valid and belongs to that particular user, will send some security code to user's email and phone.
If first step is success. Need to capture security codes(from email and phone) provided by user and validate those.
If second step is success. Capture user's new password and send a request to save that new password.
Now I can create three different Rest APIs for above three steps and perform the task whatever required for that particular API.
If I am calling this forget password from a UI, in that case it will be responsibility of the UI client to take care of the sequence of the API's being get called to complete the whole process. But from API perspective user can skip the initial 2 API calls and directly call the 3rd API which save/overwrite the existing password with new password.
How can I enforce the sequence of steps(APIs) being called even if consumer of the API's is not a UI rather I am using it by some Rest browser client?
You should merge both second and third step.
Basically, we should receive the validation token with the new password request to confirm if it's a valid operation.
First step, the user request a "forgot password" operation and you send the token to his email/phone. You shouldn't send that information in the response of this request for security reasons.
Second step, the user introduce the token (or open an link with the token in the querystring, etc) and introduce the new password. The request that will go the API will contain both the token and the new password. If the token is valid for that user, you must update the password.