I'm trying to implement a multi-step registration process. This particular implementation involves OmniAuth, but for the purposes of this discussion it could well be any OAuth provider.
Here are the registration steps we might implement:
Step 1. User clicks Connect with _________.
Step 2. Authorize via _________ and redirect to callback. Pull relevant data from provider and save to your User model.
Step 3. Pick a username, fill out email address, and any other required data for registering for your app. Also save this to your User model.
The problem for me lies in the transition from Step 2 to Step 3. I could set validations on the data based on state, using something like state_machine, for example.
However, I would have to remove not-null constraints at the column level for certain essential pieces of data, like username in my case (which is used for user routes), or email. I'm not super comfortable removing another layer of data integrity.
I'm sure this problem has been solved a thousand times before. How have you handled it and how would you recommend I handle it?
Could you add the data from the provider as hidden fields in your form for step 3 to pick username, email address etc? This way you wouldn't be creating a User instance until you had all of the data available.
Related
I need to develop a REST API for a new user registration form which consists of 2 steps. Step 1 validates email and password requirements. The data gets saved into the database on Step 2 after the user specifies valid country and region. What is the recommended approach in such scenarios? Using different endpoints for step 1 and 2, like /users/password and /users/location? Using a single endpoint, but having some info about the current step as part of the Dto? Anything else?
Hi you can use both of the options i my self used both of them but in my experience using a multi endpoint is more maintainable. And also it good match for single-responsibility principle (SRP)
i am making a set of applications that share a common oidc provider (in my control), where the users will be created.
One of my applications is a stateless SPA "meeting" app where you can schedule meetings with other users, and you login purely by an OIDC token.
I am having a hard time thinking a strategy about the following
Should the "user" details be stored in the meeting app after a login? So let's say user A exists in the provider, then enters the meeting app. Should i save user A in the meeting app DB?
How to handle change of user details? Let's say user A changes name to User B in the provider. Until he logs in again, all the other users see him as User A still in the "contacts" list. What is the usual practice for solving this?
How to handle deletions in the provider. I need someway to signal that "deleted in provider -> deleted in app". Should i constantly poll the provider and get any missing users, create a push system, or is this just unneeded?
Thanks a lot in advance
That's actually a very good question and rarely explained well in online articles. Hopefully the below detailed notes help you with your solution. I have answered your questions at the end.
OAUTH USER DATA
Typically the core user data such as name, email etc belongs in the Authorization Server. It contains Personally Identifiable Information (PII) and changes are audited there. This is explored in further detail in the Privacy and GDPR article.
DOMAIN SPECIFIC USER DATA
This might include fields like a user's application preferences, and you may end up with data similar to this in your APIs:
Field
Description
id
A database surrogate key for the user
subject
The subject claim from an OAuth access token, which is typically a GUID or something similar
archived
A boolean flag set to true when a user is active in the app
field 1
A domain specific value
field 2
A domain specific value
To get OAuth user data within your applications your APIs can call the Authorization Server's SCIM 2.0 endpoint as described in this User Management article.
AUTHORIZATION AND ROLES
Interestingly, roles and application specific rights could be stored in either of the above data sources. You may want to start by putting roles in the OAuth data, but for cases where they are very domain specific and change often, I have found that storing them in my own API data works best.
DOMAIN SPECIFIC USER DATA AND ACCESS TOKENS
Sometimes you need to include domain specific user data (which might include roles) in access tokens. This Claims Article explains how claims can be looked up from external APIs during token issuance. This typically involves a REST call from the Authorization Server to one or more APIs, providing the subject value for which tokens will be issued.
CONSISTENT USER IDENTITY IN YOUR APPS
A user can potentially authenticate in multiple ways, such as default password / corporate login / social login. You may need to use some custom Account Linking logic to ensure that the subject field in the access token gets the same value in all cases. This prevents you ever creating duplicate users within your application.
USER INFO CHANGES
These are typically made by end users within an application screen, and your APIs then call SCIM endpoints to update the core OAuth data. A common case is when a user changes their name and / or email, eg if the user gets married. Note that the subject value remains the same after this edit.
USER ADMINISTRATION
In scenarios where corporate assets are used, an administrator typically provisions users, either individually or in bulk. This can be done via the SCIM endpoint. In some cases administrator actions may need to save data to both data sources - eg to create a user and set roles + application preferences.
USER INFO EVENTS
Sometimes your application needs to know about a user info event, such as new, deleted or changed users. This can be managed via Event Listeners, where an extension to the Authorization Server calls back your domain specific APIs when a user edit occurs. When a user is deleted in the OAuth user data you might then update the user's application state to archived.
DATA MIGRATIONS
Finally it is worth mentioning that the above also supports migrating to an OAuth architecture or between providers:
Get a combined view of the user data before migration
Insert all existing users into the new OAuth system via SCIM
Update the combined view of the user data with new subject values
Update your domain specific data with new subject values
SUMMARY
So to answer your questions:
Aim to avoid this because it adds complexity, though in some cases you may need to denormalise for performance reasons. The OAuth user data should remain the source of truth and the only place where edits occur to PII data.
Your meeting app would need to join on the OAuth user data and domain specific user data and present a list. This would probably involve caching a combined view of the user data.
See Administrator Events above. Your API should be informed of OAuth user data changes via an event, then your SPA would get current data on the next refresh.
When implemented like this you end up with simple code and a well defined architecture. Some providers may not provide all of these features though, in which case you may need an alternative approach to some areas.
Maybe I am missing something here, or this is a flaw in Auth0? I'll give an example of my issue:
Let's say I have my Default App (client), hooked up to that I have Username-Password-Authentication (database connection) and google-oauth2 (social connection).
A user comes to my site, signs up via google-oauth2 (social connection) using joe#gmail.com. Once complete, he gets added to my users in Auth0, all great.
A few months later, Joe comes back to my site, and being a busy guy, he forgets he signed up to my site before. This time, he decides to sign up using my custom Email and Password form, that will add the user to the Username-Password-Authentication (database connection). so he signs up there using joe#gmail.com again, and everything goes well, he is now listed in my user's section in my Auth0 dashboard.
This is the problem, I now have two joe#gmail.com accounts, one with google-outh2 and one with Username-Password-Authentication. I really can't have this, I need a unique email address, regardless of the ID Auth0 supplies.
Does anyone know how I can make email address in my user section 100% unique? I'd think a rule would do this, but it appears rules only apply AFTER a user has been registered, so I can't run a rule before adding?
the only way I can see doing this right now is make my own checks and delete via the management API, but that is a really long and messy way to do it I feel.
Any help will be appreciated here!
Thanks!
Auth0's default behavior is to create a new account in the database for every unique entry. Since the user created using Google has a unique id (based on google-oauth2), and the user created using the sign-up form has a unique id - they will technically be considered two separate accounts. In order to resolve this disparity, you can establish a means with which the account data can be merged. In the documentation linked provided above, there are examples of three possible ways of doing this:
Automatic Linking - which involves creating a specific rule to merge users based on matching emails
User-Initiated Linking - which involves providing a UI for users to opt into merging users with matching emails
Suggested Account Linking - which involves setting up a rule that is linked into the UI
One important thing to consider is that the data returned from different social identity providers may not be normalized the way that data is normalized onto the Username-Password-Database. For example, while auth0's default for emails is to lowercase the information, google-oauth2 may return emails as Uppercased - creating the potential for non-matching emails when checks are made using strict equality
The option you are looking for is called account linking.
You can find more info at https://auth0.com/docs/link-accounts
I have a "chicken egg" problem.
In application I use UserDetailsService to get User (we don't store user information in our DB, we use third party service to actually get all information).
Recently we've added account activation feature. After registration, we send an activation email to a user and if he clicks on it, we mark the User as ACTIVE and redirects him to log in page. User can login only if he has ACTIVE status. The problem is: we'll start charging user from the date he activates his account even if he never logs in. How can I (maybe using spring security) make those processes (activation and login) almost simultaneous? We don't want to charge user if he just activates his account, we want to charge him only if he has logged in (after activation). So can I actually do it somehow "user clicks activation link, login and then his status is changed to ACTIVE (but he can login only if he is ACTIVE)".
Sorry if my problem description isn't clear enough
I'll appreciate any feedback.
Thanks!
If I understood your requirements correctly, you'll need two different entry points (login pages) to your application:
One for activation (first login) for users not yet activated.
Another "normal" one for active users.
The problem is that the authentication logic would need to be context sensitive and be aware of which of the above pages initated the authentication. However the framework was not designed for such uncommon use-cases, so the authentication provider has no knowledge about the URL from which the login-form were actually sent.
What you need to solve is to somehow relay contextual information to an authentication provider that processes the auth request according to that information (i.e. authenticate only non-active users logging in from url1, and authenticate only active users logging in from url2). There could be hundreds of different ways to achieve this, one possible solution is to put two different authenentication filters in place that intercept auth requests sent to the two different urls. Details outlined below:
Create your own custom versions of the existing WebAuthenticationDetailsSource and WebAuthenticationDetails (preferably by subclassing the latter) that stores and exposes the URI of the authentication request. (That will be the contextual information based on which the auth provider can implement its conditional logic.)
Configure and insert two different instances of the UsernamePasswordAuthenticationFilter in the filter chain. Set their filterProcessesUrl attribute to /j_spring_security_check_active_user and /j_spring_security_check_nonactive_user respectively, plus inject the above created custom AuthenticationDetailsSource in both of them.
Override DaoAuthenticationProvider.additionalAuthenticationChecks() in a subclass in the following way:
Retrieve the URI stored in the above created WebAuthenticationDetails object (it's accessible via authentication.getDetails())
Assert that the user is active/non-active according to the URI, and throw an AccountStatusException if the asserion fails.
Don't forget to delegate to the superclass if the assertion succeeds.
Create the two different login pages mentioned at the beginning of the post, making sure that the login forms post credentials to their respective URL (/j_spring_security_check_nonactive_user vs. /j_spring_security_check_active_user).
I've got a wizard which needs to validate if a user is logged in and then at the end validate that all the details they have entered are correct.
The issue is I'm not sure where to put the validation logic.
At the moment I have a BuyMembership class which has validation on the Buy() method.
However this won't be called at the beginning of the wizard where I need to validate whether the user is unique and is eligible for buying a membership.
For this validation I've created a BuyMembershipValidation class which validates if the user is eligible.
The issue now is that I have to pass a different parameter object to the BuyMembershipValidation and the BuyMembership classes. This means the data is split.
Is there a better way of doing it. Should I only load part of the information into the BuyMembership class for the initial validation and then load the rest after?
Update:
I need to validate when they enter the wizard (check if they are logged in already) and if they aren't then they will register as a new user otherwise I have to check if they have the right settings to buy membership as all users can't buy a membership. That's why I need two sets of validation. One for whether they're eligible and another for the actual data they enter to register. However I would like to recheck that they are eligible when they make the final transaction just in case they somehow made it past the first wizard step or if the web service (where I'm doing the logic) is called from somewhere else at a later point.
Another Update:
I've added an answer with my decision.
TIA,
Jonathan.
You're actually talking about three very different things here, and like Allain said, you need to think along the lines of single-responsibility to do this right, OOP-like.
Authentication: making sure a user is known
Authorization: making sure a user can access certain logic in your application
Validation: making sure the data a user inputs is valid/safe/what's expected or needed
A validator is typically tasked with validating user supplied information, not authorization. The single responsibility principle dictates that in your case they should definitely be handed by two objects.
Passing different parameter objects is fine, but but I think the authorization one shouldn't really be passed in to the BuyMembership object, it sounds like something that should be handled externally to it.
My 2 cents.
User authentication is a broad topic. It really depends on what type of application you are building. If it is a web application, I would recommend using one of the standard methods for authentication: NTLM / kerberos
As to when to validate, I don't see any problem with holding off on the validation until the actual Buy method is called. As long as during the steps in the wizard, you are not releasing any information to them and are only collecting data from a form. The main time when you need to be concerned about authentication is when you are actually placing an order or releasing information to the user that should be locked away in a database somewhere.
If you provide more details to what you are trying to do, I will refine my answer to help you a bit more. It's a rather broad question though, so this is the best I can come up with for now.
I've decided to go with just the one class. The reason is that there is common logic / behaviour in the IsEligible and Buy methods. One determines if the inputs meet the eligibility requirements and the other validates that all the details needed to buy the membership are valid. I don't really see this as authentication related. The authentication is done in a separate web control and I just check the Identity.IsAuthenticated and Username to see if they are logged in. I then check their username and if they are eligible to buy then I let them continue, otherwise show an error message. In terms of input validation I already have validation controls on my webpage but the validation I'm concerned with here is server side business logic validation. For that reason I think it should be in the business logic class. Not some validation class separate from the logic that performs the payment. This may just be my way of looking at it but I think this fits well with the BDD ideas of keeping validation inside the entity that holds the data.