How to identify the current user when redirected from an auth0 rule - authentication

My authentication flow is as follows...
User visits my site and clicks login.
They are redirected to mydomain.auth0.com (or something like that), where they are prompted to login using either a username or password or a social provider e.g. Google.
Following the user's login, I run a custom rule in Auth0 to detect if they are a new user. If yes I now want to redirect them to a custom page to collect some additional data. I do this my implementing the following in the rule:
function (user, context, callback) {
context.redirect = {
url: 'http://www.example.com/profile'
};
callback(null, user, context);
}
This redirects the user back to my site adding a ?state=xyz... to the query string.
However, at this point I don't have a token or id token and seemingly no way to identify the user.
The example given at Auth0 talks about using this flow to force a user to update their password on login. However, for this to work there has to be some way to identify the user on the page they are redirected to.
What is the best way to get this working?

Here is a snippet from a Rule that redirects users to a post authentication registration page. Just pasting the section related to altering context.redirect since that is what you are asking about.
var hasPostRegistered = user.app_metadata && user.app_metadata.has_post_registered;
// redirect to consent form if user has not yet consented
if (!hasPostRegistered && context.protocol !== 'redirect-callback') {
var auth0Domain = auth0.baseUrl.match(/([^:]*:\/\/)?([^\/]+\.[^\/]+)/)[2];
var userId = user.user_id;
context.redirect = {
url: configuration.REGISTRATION_FORM_URL +
(configuration.REGISTRATION_FORM_URL.indexOf('?') === -1 ? '?' : '&') +
'auth0_domain=' + encodeURIComponent(auth0Domain) +
'&user_id=' + encodeURIComponent(userId)
};
}
Notice how the user_id is tagged on as a query param.
Note also, you can self-sign a JWT token inside a Rule, and send that to your redirect endpoint, which in turn can be configured to read / verify that JWT Token as a way to secure the redirection.

Related

Keycloak - how to allow linking accounts without registration

I am managing a Keycloak realm with only a single, fully-trusted external IdP added that is intended to be the default authentication mechanism for users.
I do not want to allow user to register, i.e. I want to manually create a local Keycloak user, and that user should then be allowed to link his external IdP account to the pre-existing Keycloak account, having the email address as common identifier. Users with access to the external IdP but without an existing Keycloak account should not be allowed to connect.
I tried the following First Broker Login settings, but whenever a user tries to login, he gets an error message (code: invalid_user_credentials).
Do you have any idea what my mistake might be?
Looks like they integrated this feature in version 4.5.0.
See automatic account link docs.
Basically you need to create a new flow and add 2 alternative executions:
Create User If Unique
Automatically Link Brokered Account
According to the doc: https://www.keycloak.org/docs/latest/server_admin/index.html#detect-existing-user-first-login-flow, you must create a new flow like this:
et voilà :)
As per this discussion:
https://keycloak.discourse.group/t/link-idp-to-existing-user/1094/5
It’s a bug in keycloak and they seem to be a reluctant to fix it for
whatever reason. I have very few users so I solved it by manually
querying the idp for the information keycloak uses and then copying it
into the relevant fields in the UI. So there is no sign up process for
my users I just make them myself. Obviously that’s a poor solution
though, what we really need is someone to take over that PR and
persuade the maintainers to merge it.
This is the PR: https://github.com/keycloak/keycloak/pull/6282
As it is described in this GitHub issue response the solution is to use a JavaScript authenticator that handles this.
In order to do so, you need to do the folowing:
Enable [custom authenticators using JavaScript in your server[(https://www.keycloak.org/docs/latest/server_installation/#profiles) by https://stackoverflow.com/a/63274532/550222creating a file profile.properties in your configuration directory that contains the following:
feature.scripts=enabled
Create the custom authenticator. You have to create a JAR file (essentially a ZIP file) with the following structure:
META-INF/keycloak-scripts.json
auth-user-must-exist.js
The content of the files are in this Gist, but I am including them here as well:
META-INF/keycloak-scripts.json:
{
"authenticators": [
{
"name": "User must exists",
"fileName": "auth-user-must-exists.js",
"description": "User must exists"
}
]
}
auth-user-must-exist.js:
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError")
ServicesLogger = Java.type("org.keycloak.services.ServicesLogger")
AbstractIdpAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator")
IdpCreateUserIfUniqueAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator")
var IdpUserMustExists = Java.extend(IdpCreateUserIfUniqueAuthenticator)
function authenticate(context) {
var auth = new IdpUserMustExists() {
authenticateImpl: function(context, serializedCtx, brokerContext) {
var parent = Java.super(auth)
var session = context.getSession()
var realm = context.getRealm()
var authSession = context.getAuthenticationSession()
if (authSession.getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO) != null) {
context.attempted()
return
}
var username = parent.getUsername(context, serializedCtx, brokerContext)
if (username == null) {
ServicesLogger.LOGGER.resetFlow(realm.isRegistrationEmailAsUsername() ? "Email" : "Username")
authSession.setAuthNote(AbstractIdpAuthenticator.ENFORCE_UPDATE_PROFILE, "true")
context.resetFlow()
return
}
var duplication = parent.checkExistingUser(context, username, serializedCtx, brokerContext)
if (duplication == null) {
LOG.info("user not found " + username)
context.failure(AuthenticationFlowError.INVALID_USER)
return
} else {
authSession.setAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO, duplication.serialize())
context.attempted()
}
}
}
auth.authenticate(context)
}
Then, you can define as follows:
User Must Exist -> ALTERNATIVE
Automatically Set Existing User -> ALTERNATIVE
Honestly i am surprised by the keycloak auto creating behavior. I tried to add new Authentication flow as descibed here https://www.keycloak.org/docs/latest/server_admin/index.html#automatically-link-existing-first-login-flow
My flow :
1 - Create User If Unique [ALTERNATIVE]
2 - Automatically Link Brokered Account [ALTERNATIVE]
My use case : Authenticating users from Github ( Github as IDP )
Result : when a github user logon with an existing "username" keycloak links the github account to my local user ( based on his username ). I expected using his email instead of username.

Cookie Authentication: How to access a returning user

I have studied a couple examples of ASP.Net Core 2.0 Cookie Authentication.
However, I still don't see the code that checks for a returning user via a cookie.
Please note that I'm not a web developer. As a result, I realize that my question may appear foolish.
As I understand, the browser sends the cookie to the server per client request. However, I just don't see the server logic for this in the examples that I've studied. Hence, I only see logic for logging in the user the very first time based a username and password that's explicitly passed in.
Expectation:
I expected the server to explicitly check if a cookie exists when requesting the index page of the website. If a cookie does exist, I would expect to see some logic to address a user that's already logged in.
Giraffe example
Tutorial example
Here's my code:
let loginHandler =
fun (next : HttpFunc) (ctx : HttpContext) ->
Tasks.Task.Run(fun _ -> StackOverflow.CachedTags.Instance() |> ignore) |> ignore
task {
let! data = ctx.BindJsonAsync<LogInRequest>()
let email = data.Email.ToLower()
if authenticate email data.Password
then match login email with
| Some provider ->
let claims = [ Claim(ClaimTypes.Name, email) ]
let identity = ClaimsIdentity(claims, authScheme)
let user = ClaimsPrincipal(identity)
do! ctx.SignInAsync(authScheme, user)
return! json provider next ctx
| None -> return! (setStatusCode 400 >=> json "Invalid login") next ctx
else return! (setStatusCode 400 >=> json "Invalid login") next ctx
}
I'm looking at the giraffe example.
The statements services.AddAuthentication(authScheme) and services.AddCookie(cookieAuth) will add various services to the services collection, making them available for injection.
The statement app.UseAuthentication() adds services to the middleware pipeline. A middleware service is something that runs on every request.
The service that gets added by UseAuthentication is called AuthenticationMiddleware. As you can see this middleware relies on an injected IAuthenticationSchemeProvider (provided by your call to AddAuthentication) which in turn (skipping a few steps here) relies on a service called CookieAuthenticationhandler (provided by your call to AddCookie) which actually does the work of authenticating via cookie. When it is done it sets the logged in user onto the HttpContext which you can reference from your controllers if you need to.

Fetching Google Plus profile url and email

If I open someone's Google Plus Profile Page I see contact info and information shared on Google Plus. I looking for similar information on Google API. I'm trying to fetch list of user's contacts with email and google plus profile id, that's all.
Here I can fetch user connections with Google Plus profile url but without email or phone number.
https://people.googleapis.com/v1/people/me/connections
Here I can fetch person contacts with email and phone number (OAuth2) - without Google Plus profile url nor id
https://www.google.com/m8/feeds/contacts/{GOOGLE_ACCOUNT_NAME}%40gmail.com/full?alt=json
But I don't know how to combine this two outputs, to get have Google Plus profile url and contact information.
You are correct. To retrieve profile information for a user, use the people.get API method. To get profile information for the currently authorized user, use the userId value of me.
gapi.client.load('plus','v1', function(){
var request = gapi.client.plus.people.get({
'userId': 'me'
});
request.execute(function(resp) {
console.log('Retrieved profile for:' + resp.displayName);
});
});
Note that this method requires authentication using a token that has been granted the OAuth scope https://www.googleapis.com/auth/plus.login or https://www.googleapis.com/auth/plus.me.
Plus.People.List listPeople = plus.people().list(
"me", "visible");
listPeople.setMaxResults(5L);
PeopleFeed peopleFeed = listPeople.execute();
List<Person> people = peopleFeed.getItems();
// Loop through until we arrive at an empty page
while (people != null) {
for (Person person : people) {
System.out.println(person.getDisplayName());
}
// We will know we are on the last page when the next page token is
// null.
// If this is the case, break.
if (peopleFeed.getNextPageToken() == null) {
break;
}
// Prepare the next page of results
listPeople.setPageToken(peopleFeed.getNextPageToken());
// Execute and process the next page request
peopleFeed = listPeople.execute();
people = peopleFeed.getItems();
}
Here's a related SO ticket which discuss how to fetch user email from Google+ Oauth: How to get user email from google plus oauth
You can use the Google Api to fetch the user profile. For this
Create project in google api console.Configure the credentials client id,client secret. Add your redirect uri.
Authorize the user with OAuth2.0 from your project with scopes https://www.googleapis.com/auth/plus.me , https://www.googleapis.com/auth/plus.login.
Retrieve the response code after authorization.Give POST method to the token endpoint url.
Retrieve the access_token, refresh_token,id_token etc from gooogle plus.
By using the access_token. call GET method to the url "https://www.googleapis.com/plus/v1/people/me/?access_token='{YOUR_ACCESS_TOKEN}'".
You will be given by an json array containing the authorized user profile details like email, name, id etc.

Accounts.registerLoginHandler with passwords in Meteor

I'm new to meteor and am stuck on registering a login handler that lets me use the password to authenticate the user.
I'm working off the code from http://meteorhacks.com
The server side code is as follows:
Accounts.registerLoginHandler(function(loginRequest) {
var userId = null;
var user = Meteor.users.findOne({'emails.address': loginRequest.email, password: loginRequest.password, 'proile.type': loginRequest.type});
if(user) {
userId = user._id;
}
return { id: userId}
This works fine if I take out the password field and just use the email and type ones. How do I get this working with the password as well?
Bottom line, you can't directly search via the plaintext password. You need to verify the password via SRP which is a little tricky as there isn't any documentation on it. Luckily Meteor is open source! A good start is at the accounts-password : https://github.com/meteor/meteor/blob/master/packages/accounts-password/password_server.js
There already is a package that can do password logins for you (the one the above file is from). You can add it to your project via meteor add accounts-password.
Then you could login with Meteor.loginWithPassword

In coldfusion: how to remember that a user has logged in?

I have a username/password on a login page, which goes to 'member' page. Basically, I used a statement that finds the number of rows in a SQL query, and if it's not 1 (the correct username/password combination), then it redirects back to the login page.
However, on the 'member' page, there are forms to do various things, like add new rows to a table using SQL, or return queries of joined tables. These forms also link back to this 'member' page, but the conditions for logging in (which requires a username variable and password variable) would no longer be met.
So, how do I get the server to remember whether a user is logged on or not?
In the application.cfm or application.cfc, you will need to enable sessionManagement = true so you can start tracking variables across page requests. Then when a user logs in, you will set a variable like isAuthenticated = true. Only redirect to the login page if the isAuthenticated = false or undefined (you can set a default using cfparam in onSessionStart of Application.cfm)
Rough example, assuming you are using ColdFusion 9+
Application.cfc
component {
this.name = 'myApplication';
this.SessionManagement = true;
public function onSessionStart() {
param name='session.isAuthenticated' default=false;
}
}
checkAuthentication.cfm
<cfscript>
if (!session.isAuthenticated) {
// send to login page
}
</cfscript>
In your login processing page, make sure you set session.isAuthenticated to true, and then it should skip checking / asking for the login. You can also look into the built-in authentication functions of CFLOGIN.
Hope that helps a bit.