Custom central access manager
Tags: octograd2020, php, cubequence
Published: 2020-05-23 10:59:18

When I came across products on the "web" which seemed really useful but weren't free (A student-budget isn't that big), I would try to build it myself, thus saving me the costs and giving me the opportunity to practice. 

## The old way
To keep these projects as simple as possible I used GitHub OAuth. Which was really easy and great for that time. But once I had about 3 of these self-made projects and a few modified forks using GitHub OAuth, I started to think that it wasn't practical to update all these repo's individually if I changed the auth logic.

![GitHub flow](https://dev-to-uploads.s3.amazonaws.com/i/y0w1a07m9lcubswdlmyr.png)

## The bad fix
I had created my own OAuth provider and client.
{% github Luca-Castelnuovo-Archive/Accounts no-readme %}
But I didn't like it. Mostly because the code wasn't well written, didn't follow the guidelines for OAuth and I didn't felt that I could keep maintaining this spaghetti code, making it obsolete in the near-future.

![Custom Flow](https://dev-to-uploads.s3.amazonaws.com/i/7a14fv9k981oyrdhq4n7.png)

## The solution
So I came to this project. It was important, and an oversight of the earlier project, to define what I wanted the project to achieve, how to achieve this, and actually achieving it. 

#### The defining part
It thought it would be a good idea to still have the central authentication and authorization concept, while maintaining the simpleness of GitHub and Google OAuth.

I had been experimenting with JWT's in this time, so instead of my provider granting `authorization_code`, I would send an JWT with claims to login. The client could then validate the claims and provide access.

During this process I realized that I wanted to provide multiple levels of access. This is where the `variant` claim comes in handy. The claim would limit certain things, like the allowed monthly API requests, files upload limit, etc.

#### The how-to part
For the authentication I would use a mix of three sign-in options. GitHub because I use it the most. Google, because most non-github users probably do have an Google account. And finally email.

Now I was pondering whether to include email, because this means implementing forgot password, verify email, and storing passwords problems. Luckily, I came across couple of posts about [magic-login](https://www.sitepoint.com/lets-kill-the-password-magic-login-links-to-the-rescue/) links, but some raised concerns like [this one](https://adam.ac/blog/magic-login-links-are-not-ok/).

![Login Screen](https://dev-to-uploads.s3.amazonaws.com/i/32lg65mx8eh363zgk7wt.png)

These concerns are a bit outside the intended scope of this article. But come down to email isn't that secure. To fix this I coupled the login link to the browser session. This had the problem that the link couldn’t be accepted on your phone and then continue on your desktop, but this was a comprise I was willing to make.

With the authentication sorted, the authorization part came.

For this Gumroad seemed like a useful solution. I would create a hidden Gumroad product for each service. These would contain tiers (variants).

![Gumroad service](https://dev-to-uploads.s3.amazonaws.com/i/23mtpete894fq8yfwqo8.png)

On successful purchase Gumroad would provide a license key which I would store in my app and use the authorize a user’s access to that service.

![Licenses screen](https://dev-to-uploads.s3.amazonaws.com/i/xeshdd7n2n87wvou8wc9.png)

Now that I was able to authenticate user with login options and authorize user with connected Gumroad license, the final part came, integration with the clients. The client would receive the login token as JWT containing the following claims:
```js
{
  "iss": "https://apps.lucacastelnuovo.nl",
  "aud": "https://mailjs.lucacastelnuovo.nl",
  "iat": 1590227858,
  "exp": 1590228458,
  "type": "auth",
  "sub": "9ea3cbd8-895a-4f9b-ac69-5f600376adbb",
  "user_agent": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20070321 Netscape/8.1.3",
  "user_ip": "151.101.130.217",
  "variant": "Professional"
}
```

These tokens would be valid for 10mins, to mitigate token theft impact.
I didn't want to use `refresh_tokens` as this would just create another unnecessary (for this system) attack vector. Instead the following flow is implemented:

![Apps Flow](https://dev-to-uploads.s3.amazonaws.com/i/a12whtdbzt0mku0703fx.png)

### The building part
To build this project I was thinking about using Laravel or another framework, but these were all quite complicated to learn quickly enough I wanted this project to be done (presentable at the least). So in typical side-project fashion, I created my own framework. Which took maybe more time to learn the basics of Laravel, but also learned me a lot of things.
{% github Luca-Castelnuovo/CubeQuence no-readme %}

The final stack I used was:
- Server: LAMP
- Backend: CubeQuence (PHP)
- Frontend: HTML, CSS (MaterializeCSS), JS (vanilla)

## Links
Demo: https://apps.lucacastelnuovo.nl
Code: {% github Luca-Castelnuovo/Apps no-readme %}

## Additional Thoughts
It was really fun to write this article, i'm not really a writer so this was a difficult but educational experience.