Authentication
Authentication means to verify who you are.
Basic Auth
Sensitive data required for login is encoded with Base64. Base64 is very easy to decode. Not recommended and probably the least secure authentication method, but easy to implement.
JWT Authentication
- It is difficult to invalidate users who can only be authenticated with JWT, without invalidating all users.
- Sensitive data required for login is converted to a JSON Web Token.
- This is not much better than the Basic Authentication in terms of actual security - JWT can be decoded easily.
- If a hacker fetches your JWT somehow, the hacker can now do whatever they want on your behalf because they are "authenticated"
- It's completely stateless, just like Basic Authentication, which means it scales very easily.
Sessions
- You can revoke the session anytime, or control access permissions much easier, which is a plus
- Traditionally stateful, since you are storing session IDs into a single app server machine. This leads to issues when you scale up the number of servers, since the load balancer might direct your request to an app server that doesn't recognize your session ID. To achieve stateless authentication, you can (and should) store session data into a distributed cache database (like Redis) so that any one of your application servers can verify the session.
- Depending on where you store session state, inconsistencies and replication might still occur (what if I change my password on an account and I try to login with the old password immediately after?)
- It can be more secure than completely stateless counterparts since hackers can't manipulate client stored authentication tokens to do malicious things. On the flip side, if hackers breach the session database or the authentication server, then all user info is leaked.
OAuth 2.0
- OAuth 2.0 is only useful if you want to authenticate on behalf of a client between third party OAuth providers (Google, Facebook, Apple, etc.)
- For example, if you don't want to implement your own login system and rather authorize access to clients who are signed in to Google, Facebook, or Apple.
- Holistically it is not a replacement for JWT or session based management.
- In the generic flow, you request for an authorization code passing in information related to the client to verify its entity. The authentication server takes the authorization code and exchanges it for an access token and a refresh token, which can be used for authorization in APIs that you may want to protect.
- You can create your own OAuth service, which handles authentication with the OAuth 2.0 flow and generates access tokens and refresh tokens on behalf of your clients (i.e. the application that has the Login UX). This can be quite complex and time consuming to build from scratch, even with the use of open-source OAuth libraries for your favorite serverside framework.
Authorization
Authorization is to grant you access permissions on resources after Authentication.
IDs
IDs can be generated with many algorithms:
Auto Increment
- Comes out of the box in most SQL databases
- Doesn't scale when you increase number of machines due to ID inconsistencies
UUID
- ID is guaranteed to be unique, probability of creating just one duplicate is extremely low
- It's a randomly generated ID, so no input required
- Can scale as you increase number of machines
- Its 128 bits so it can be too big
SHA256
- It's a reliable and fast hash
- It's a hash so you need some input (i.e. you could hash an auto-incremented ID)
- Still have to worry about hash collisions
- It's even bigger than UUID (256 bits)
Twitter Snowflake
- It's compact with only 64 bits
- It's a time ordered ID, so some databases can take advantage of this property
Passwords
Note: If you are using 3rd party OAuth providers for authorization, and not storing user ids and passwords yourself, then you don't need to read on.
Allowing your users to create an account and a password means that you will need to persist that data into some sort of data store. Typically, a relational database does a good job at storing user information. Some SQL databases come with salt and password hashing functions built-in, so that you don't re-invent the wheel. With that said however, basics of password hashing should be understood:
- Passwords are almost always best hashed and not encoded or encrypted in such a way that it can be decoded or decrypted, respectively. In cases where passwords have to be decrypted, extra caution must be taken on the decryption functionality and where the decryption key is stored.
- Use good password hashing functions, not the basic and fast hashing functions like MD5 or SHA256.
- Hash multiple times over
- Use salt and pepper for your hash browns - salt is a random generated ID for the user, pepper is a random generated ID for the application/service
Example hash function with salt:
import hashlib, uuid
salt = uuid.uuid4().hex
pepper = os.environ['APP_PEPPER_ID']
hashed_password = hashlib.PKCS(password + salt + pepper).hexdigest() # don't use basic SHA-256 or MD5
Also see Storing Passwords for more details.