Go-Auth-Service

module
v0.0.0-...-85dfee8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 22, 2021 License: MIT

README

Go Authentication Service (Golang + PostgreSQL + Redis)

This project is an authentication service written in Go that handles the following responsibilities:

  • User registration, user logins, and storing user credentials.
  • Issuing JSON Web Tokens (JWTs) for session-management purposes. Tokens are passed to the client-side as cookies.
  • Generating and storing API Keys.
  • Verifies on behalf of other services if JWTs and API Keys are valid. Service will return the user ID if valid.

User Authentication

The primary function of the authentication service is to handle the storing of user credentials, verifying user credentials, and issuing JSON Web Tokens (as cookies) to users who provide correct credentials. All of the user information is stored in a PostgreSQL database.

Authentication Flow
  1. Login by submitting a POST request to "/auth/login" with a valid Email and Password combination.
  2. Successful user login generates two JSON Web Tokens:
    • Access Token: Valid for 15 minutes, the Access Token should be used to provide privileged access to other services.
    • Refresh Token: Valid for 24 hours, the Refresh Token's only purpose is to generate new Access tokens.
    • The signed tokens are returned to the client as values in the cookies "access_token" and "refresh_token".
  3. New access tokens can be generated as needed by submitting a POST request to "/auth/refresh":
    • The service will check the Refresh Token included in the "refresh_token" cookie. If it is valid, a new Access Token will be generated and passed to the client-side in the "access_token" cookie.
    • If the Refresh Token is no longer valid (ex: 24 hours have elapsed), the client will need to re-authenticate to "/auth/login".
  4. Logout by submitting a POST request to "/auth/logout".
JSON Web Token Blocklist

By design, it can be difficult to invalidate JSON Web Tokens from the server-side without queries to the database or maintaining individualized JWT Secrets. The service implements a JSON Web Token Blocklist stored in Redis in an attempt to overcome these limitations.

Any time that a user logs out of the service, the service will extract the signed JWT strings from the "access_token" and "refresh_token" cookies (if available) and then add them as keys to the Blocklist. And, any time that a user refreshes their Access Token, it will add the signed JWT string from "access_token" to the Blocklist.

The Blocklist is consulted whenever the authentication service checks if a JWT is valid. Access Token strings are kept in the Blocklist for 15 minutes. Refresh Token strings are kept for 24 hours.

API Keys

If needed, the authentication service can be used to generate and store API Keys for users. By design, it will only store a single API Key per user in the database.

Generating API Keys
  1. Generate a new API Key by submitting a POST request to "/auth/apikey".
    • Only requests with a valid Access Token stored in the access_token cookie will be allowed to generate an API Key.
    • If the associated user already has an API Key in the database, it will prompt the client to delete the current API Key before proceeding.
  2. The new API Key is returned to the client, and then hashed (HMAC-SHA256) and stored in the PostgreSQL database.
    • Because we only store the hash of the key, it is not possible to retrieve an API Key after it has been generated.
  3. Users can delete their API Key by submitting a DELETE request to "/auth/apikey".

JWT and API Key Verification

The authentication service exposes two API Endpoints that can be used by other services to verify that a provided API Key or "access_token" cookie is valid. If valid, it will return the user's ID to the services.

Verifying the Access Token

It is important that you pass any Access Token received from the client-side to the "/auth/claims" API endpoint before making note of the User ID or passing the client request off to a protected service.

  1. Issue a GET request to /auth/claims?token=123456789, where token is the signed JWT string extracted from the "access_token" cookie.
  2. The service will verify the signature and consult the JWT Blocklist. If valid, it will extract the Claims from the token and return the data as JSON. Returned claims will include the "userId", which contains the User ID associated with the Access Token.
Verifying API Keys

Similarly, API Keys should be checked against the "/auth/verify" API endpoint before passing the client request to any protected services.

  1. Issue a GET request to /auth/verify?key=123456789, where key is the API Key extracted from the request's HTTP Headers.
  2. The service will hash the provided API Key and look for any matches in the database. If it finds a match, it will return the User ID associated with the API Key as JSON ("userId").

API Endpoints

POST /auth/register

Register a new user account. The service only expects an email and password. Implementing additional fields (DisplayName, FirstName, LastName, etc.) should be done on a separate service that can map to the user ID.

Sample Request
curl --request POST 'http://localhost:4000/auth/register' \
--header 'Content-Type: application/json' \
--data-raw '{   
    "email": "fakeman@gmail.com",
    "password": "teS123x!!4^4"
}'
Sample Response
HTTP/1.1 201 Created

{
    "email": "fakeman@gmail.com"
}
POST /auth/login

User login. If successful, returns access_token and refresh_token cookies.

Sample Request
curl --request POST 'http://localhost:4000/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{   
    "email": "user@email.com",
    "password": "!Test!1234!"
}'
Sample Response
HTTP/1.1 200 OK
POST /auth/logout

User logout. Returns expired access_token and refresh_token cookies to overwrite any pre-existing cookies.

Sample Request
curl --request POST 'http://localhost:4000/auth/logout' \
--header 'Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzE3NDE5MjUsInVzZXJJZCI6IjAyZThjOWQ2LTU1YzYtNGQ2MS04ODM0LTg0ZmU4YjlhMThhMCJ9.LcEve-kpwXKlsOAO5V-6_dHSqRiObyCcEnfBm1u1YgI; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzE2NTY0MjUsInVzZXJJZCI6IjAyZThjOWQ2LTU1YzYtNGQ2MS04ODM0LTg0ZmU4YjlhMThhMCJ9.Fwq95sARHAuX-tDGoCXdjvMMLOG9l_w9SaZGc7HWU5M'
Sample Response
HTTP/1.1 204 No Content
POST /auth/refresh

Refreshes the Access Token and returns the new token in an access_token cookie. Client request only requires a valid refresh_token cookie in order to be successful. Including the access_token cookie is not mandatory.

Sample Request
curl --request POST 'http://localhost:4000/auth/refresh' \
--header 'Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzE3NDI0NzEsInVzZXJJZCI6IjAyZThjOWQ2LTU1YzYtNGQ2MS04ODM0LTg0ZmU4YjlhMThhMCJ9.omM7ycOxUHC_etWlTrfbyBASicPlYnPtQZrY-6jBQ-A; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzE2NTY5NzEsInVzZXJJZCI6IjAyZThjOWQ2LTU1YzYtNGQ2MS04ODM0LTg0ZmU4YjlhMThhMCJ9.lFDce-4o6gh2jnKemAnf8HoWena-a6OcQk4vdczpZLo'
Sample Response
HTTP/1.1 200 OK
GET /auth/claims

Extracts the JSON Web Token string from the ?token= query parameter. If the token is valid (not modified, etc.) then service will return the claims embedded within the token as JSON.

Sample Request
curl --request GET 'http://localhost:4000/auth/claims?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzE2NTU2OTYsInVzZXJJZCI6IjAyZThjOWQ2LTU1YzYtNGQ2MS04ODM0LTg0ZmU4YjlhMThhMCJ9.vFFeYSUYxoOYF8aozuStnwC2qqgx8cW3nnX6SXth01s'
Sample Response
HTTP/1.1 200 OK

{
    "userId": "02e8c9d6-66c6-4d61-9934-84fe8b9a18a0",
    "exp": "1.631655696e+09"
}
POST /auth/apikey

Generates a new API Key for the user if one does not already exist. Client request only requires a valid access_token cookie in order to be successful. Including the refresh_token cookie is not mandatory.

Sample Request
curl --request POST 'http://localhost:4000/auth/apikey' \
--header 'Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzE3NDExOTYsInVzZXJJZCI6IjAyZThjOWQ2LTU1YzYtNGQ2MS04ODM0LTg0ZmU4YjlhMThhMCJ9.9tW7SFQgxYImJLkFLrLuFiHg3rr2OihLcnfNSXETmk8; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzE2NTU2OTYsInVzZXJJZCI6IjAyZThjOWQ2LTU1YzYtNGQ2MS04ODM0LTg0ZmU4YjlhMThhMCJ9.vFFeYSUYxoOYF8aozuStnwC2qqgx8cW3nnX6SXth01s'
Sample Response
HTTP/1.1 201 Created

{
    "apiKey": "NaLft36C4EfgcjMMKCQP0udJAZvEspu_pOFIG96rCfQ",
    "msg": "Your new API key has been generated. Please save this key. It is not possible to recover this key."
}
DELETE /auth/apikey

Deletes the API Key associated with the requesting user. Client request only requires a valid access_token cookie in order to be successful. Including the refresh_token cookie is not mandatory.

Sample Request
curl --request DELETE 'http://localhost:4000/auth/apikey' \
--header 'Cookie: refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzE3NDExOTYsInVzZXJJZCI6IjAyZThjOWQ2LTU1YzYtNGQ2MS04ODM0LTg0ZmU4YjlhMThhMCJ9.9tW7SFQgxYImJLkFLrLuFiHg3rr2OihLcnfNSXETmk8; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzE2NTU2OTYsInVzZXJJZCI6IjAyZThjOWQ2LTU1YzYtNGQ2MS04ODM0LTg0ZmU4YjlhMThhMCJ9.vFFeYSUYxoOYF8aozuStnwC2qqgx8cW3nnX6SXth01s'
Sample Response
HTTP/1.1 204 No Content
GET /auth/verify

Extracts the API Key string from the ?key= query parameter. If the API Key exists in the database, then service will return the user ID associated with the API Key.

Sample Request
curl --request GET 'http://localhost:4000/auth/verify?key=NaLft36C4EfgcjMMKCQP0udJAZvEspu_pOFIG96rCfQ'
Sample Response
HTTP/1.1 200 OK

{
    "userId": "02e8c9d6-66c6-4d61-9934-84fe8b9a18a0"
}

Directories

Path Synopsis
internal
pg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL