No idea if I'm going the right direction

Hey everyone,

I’m Joost and I am a student at the University of Applied Sciences in Utrecht. I am one of the board members of the Study association. I am responsible for the group developing software. We don’t exist for a long time, so we don’t have a solid base to extend. That’s what I am trying to achieve, a good base that the next generation of that group can continue with that software and create it even more beautiful.

More backstory: We used to have a monolithic C# application. The downside is that C# is only taught at quarter 4 at the second year, and in the third year you are going on an internship, so in reality, you have no C# developers. So that’s how we decided to do microservices. Because that makes the code easier to get and that way we keep our extendability high.

Now the problem:

We are going to have some microservices: A member database (because we need to store all the member data, for promotion and for other purposes. We also are going to have a microservice responsible for job/intern positions, one for the activities we organize, and maybe a blog of some sort.

I already build the microservice for the member database. I built it in Spring Boot, since we get Java taught at school, and it’s fairly easy to get. The only thing I was missing was authentication. I spend 2 weeks staring at ways to create my own identity provider, but I found it was too big for this use case. So I was looking at both Okta and Auth0, I chose for Auth0 for the unlimited applications.

The way I want to organize my authorization is with RBAC (if I recall correctly: User has a role, role has permissions, with permissions you can access endpoints(correct me if I’m wrong)). Since the default tutorial is talking about having all your permissions for the endpoints stored in SecurityConfig. I would rather have the permissions for the endpoints stored at the endpoints itself, that’s done with Spring Method Security. The only documentation I found is a blog post in Groovy. I got it to work, but now I am very scared that the way I am going isn’t the right (secure) way to go.

The problem is now with my member database. When people register at our study association (which I will move online). I have several ways to go, I don’t know which one is correct.

  1. Users sign up at Auth0. Auth0 passes that data to my member database (it’s an application btw). Then my secretary will accept it, and the user is fully signed up (after paying).
  2. My users sign up at my member database application, which will send data to Auth0 to create a new user.

The thing I am not certain about is for example, what IDs to use. Because Auth0 gives me an ID like google-oauth2|342342452345345, and my backend uses a normal ID (1, 2, 3, 4,…). What ID do I need to use in my backend? Do I need to store my normal ID in the User metadata and use that to identify and get data at my member database application? Because I still need to comply to GDPR (and I guess google-oauth|2234234 is a user data).

I am also not sure about the way I implemented my endpoints, for example

    @PreAuthorize("(hasPermission('read:memberships') && isUser(#userId)) || hasPermission('read:all_memberships')")
    public Response getMembershipsByUserId(@PathVariable Long userId) {
        return new SuccessResponse(membershipService.getMembershipsByUserId(userId)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList())
        );
    }

isUser() is a function to find the user by their Auth User ID (google-oauth2|123123123). So this way I could see if the user is the user requesting, is the user the application wants data from. But if they arent, then it checking if the user is a board member (they get access to all membership data). And if they is, then they will allow it.

I think I am formulating my problem a bit vaguely, but I don’t know how to fully explain my problem. You can view the source code of my member database application at https://github.com/svIndicium/lit/tree/auth

Thank you for reading

Joost Lekkerkerker

Hi Joost.
There could be multiple “right” ways to structure this as is generally the case with software architectures, so I guess it’s better to explain what’s available on the Auth0 side and leave the decisions to you to avoid going into a deep rabbit hole.
When a user signs up in Auth0, there’s by default no authorization performed. For Social Connections the case is even stronger, as there’s no real “sign up”: it’s just a user logging in for the first time in your Auth0 domain.
We do provide the ability to store arbitrary data in the user profile stored in Auth0 (see Metadata). One piece of metadata could be your “member ID”. You can take that information and send it to the application as part of the claims of the ID token and access token (if using API authorization) with a rule that sets Custom claims:

function(user, context, callback) {
  user.app_metadata = user.app_metadata || {};
  const memberIdClaimName = "https://yourdomain.com/memberId"; // this can't be an auth0 domain
  // add the custom claim to the outgoing tokens
  context.idToken[memberIdClaimName] = user.app_metadata.memberId;
  context.accessToken[memberIdClaimName] = user.app_metadata.memberId;
  callback(null, user, context);
}

The app will then receive the member Id as part of the claims, and you can directly check for that in your applications or backend APIs. Now the first time a user logs in (signs up) there will be no member Id. Your application can detect that (no “memberId” claim) and send the user to a registration flow. Once the flow is completed (membership approved, paid for, whatever) you can use the Management API to update the user profile and add the member ID.

Does that help?

Since we are going to do more applications, the part where you send the user in a registration flow should be demanded from Auth0. What I now thinked of was:

User logs in.
If no Service ID is present:
Auth0 announces to my member database that a new user is going to be added, with the token supplied. Then the user gets redirected to the frontend. Where he will fill in or confirm his data. Then the member database validates that data. Then when the user gets redirected to Auth0. Auth0 will send another request to the member database to say like: hey, they registered. So then the actual user will be created, and the service ID will be send to Auth0 and be stored in the app_metadata of the user.

Doing this my memberdatabase can confirm that I walked through the sign up part. So I know every entry in the member data base has walked through this sign up flow.

Is this logical? I think so, are there any things you can may point at and improve this idea?

Thank you :slight_smile:

PS: Why would someone want their member ID in a somedomain.com/1 format? That’s harder to use right?