OAuth

Loading "Intro to OAuth"
Authenticating with third parties is very common in web applications. For some apps it's used to help to reduce the friction of signing up via social login (login with ๐•, Google, GitHub, etc.). For others it's to be able to retrieve or update data on behalf of the user. And still for some it's a customer requirement (SSO).
Doing this securely is challenging. So various standards have been created to make it easier to do this securely. The most common of these is OAuth2 (and its sibling OpenID Connect). These standards define a flow for authenticating with a third party and getting back an access token that can be used to make requests on behalf of the user.
The specification is beyond the scope of this workshop, but Digital Ocean has a good introduction to OAuth 2 if you're interested in diving a little deeper.

App Authentication flow with OAuth

Here's a flow diagram showing how our application will use OAuth to authenticate a user:
A flow diagram showing a user, server, provider, and connection in a database
Here's how the flow goes:
  • The user clicks "login with provider"
  • The server redirects the user to the provider (and sets a cookie to keep track of the user so we remember them when they come back).
  • The user authenticates with the provider
  • The provider redirects the user which triggers a request to the server with a code and some information to associate the user with their cookie.
  • The server exchanges the code for an access token and uses that to get the user's profile.
  • The server gets or creates a connection in the database for the user if the user already exists and redirects them home
  • Otherwise, the server redirects to onboarding with a value in the verifySession that verifies the user owns the email address with which they authenticated on the provider.

remix-auth

Implementing OAuth is definitely not most developers' favorite pass time, which is why I'm grateful to have remix-auth which makes it easy to implement various authentication strategies in your application. One of these strategies is OAuth!
First, you create a session object for remix-auth to manage the flow with the user:
import { createCookieSessionStorage } from '@remix-run/node'

export const connectionSessionStorage = createCookieSessionStorage({
	cookie: {
		name: 'en_connection',
		sameSite: 'lax',
		path: '/',
		httpOnly: true,
		maxAge: 60 * 10, // 10 minutes
		secrets: process.env.SESSION_SECRET.split(','),
		secure: process.env.NODE_ENV === 'production',
	},
})
Then you create an authenticator that will manage the flow with the provider. There's a general remix-auth-oauth2 package which can handle OAuth2 providers, but there's even a GitHub-specific strategy we can use as well! remix-auth-github:
type ProviderUser = {
	// ... user data stored in the session
}
export const authenticator = new Authenticator<ProviderUser>(
	connectionSessionStorage,
)

authenticator.use(
	new GitHubStrategy(
		{
			clientID: process.env.GITHUB_CLIENT_ID,
			clientSecret: process.env.GITHUB_CLIENT_SECRET,
			callbackURL: '/auth/github/callback',
		},
		async ({ profile }) => {
			// convert the user's profile into what the user object should be
			return {
				// ... user data stored in the session
			}
		},
	),
	// name the strategy:
	'github',
)
This would go in the action of the route that handles when the user clicks "Login with GitHub."
return await authenticator.authenticate('github', request)
// throws a redirect to GitHub + sets a cookie with some state
This would go in the loader of the route that handles when the browser is redirected from the third party back to our application.
// GitHub redirects with the code and the state value
const data = await authenticator.authenticate('github', request, {
	throwOnError: true,
})

Configuring OAuth Providers

One of the common requirements of OAuth providers is application registration. This helps the provider know who is using their service and what they're using it for. This is usually done by creating an "application" with the provider and getting a client ID and client secret. These are used to identify your application when it makes requests to the provider.
You can create an OAuth application with the GitHub API by following the GitHub documentation.
You do not have to set up a GitHub application for this workshop if you don't want to. Very early on we'll start mocking out the GitHub API so you can continue to work on the workshop without having to set up a GitHub application. But feel free to try it if you like!
If you do create an OAuth application, you'll want to set the callback URL to: http://localhost:4000/auth/github/callback.

Single Sign On

In many business-to-business or Software as a Service applications, your customers may wish (or require) that you enable their employees to access your application using their company's identity provider. This is called Single Sign On (SSO) and is a common requirement for enterprise applications.
In times past, the most common way to do this was to use SAML. Many organizations still use SAML, but it's a bit of a pain to implement. So many organizations are moving to OAuth2 and OpenID Connect.
From the How OpenID Connect Works article on the OpenID Connect website:
OpenID Connect is an interoperable authentication protocol based on the OAuth 2.0 framework of specifications (IETF RFC 6749 and 6750). It simplifies the way to verify the identity of users based on the authentication performed by an Authorization Server and to obtain user profile information in an interoperable and REST-like manner.
If you do have to support SAML, you may find remix-auth-sso helpful.
For OpenID Connect, you can use web-oidc which also comes with a remix-auth strategy.
In this exercise we're going to focus on OAuth2 with the GitHub strategy, but by the end of the workshop we'll have a module third party authentication setup which will make it easy for you to implement authentication with other providers (including SSO).