npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@spoonjoy/redwoodjs-dbauth-oauth-api

v2.0.1

Published

Server-side OAuth communication and database management for RedwoodJS projects using dbAuth. Follows the dbAuth integration pattern and reuses some of its functionality and configurations.

Downloads

18

Readme

API Package for RedwoodJS dbAuth OAuth Plugin

Table of Contents:

Overview

Welcome to the heart of the RedwoodJS dbAuth OAuth Plugin - the api package. This package is engineered to handle the server-side logic of your OAuth authentication process.

Whether you're a seasoned developer looking to streamline your OAuth integration or a beginner just getting started, this package is designed with simplicity and efficiency in mind. By closely following the dbAuth integration pattern, it initializes a class, OAuthHandler, that encapsulates all the OAuth business logic and configuration parameters. This package also leverages the DbAuthHandler instance, reusing its functionality and configurations to ensure a seamless and efficient authentication process.

Get started with the setup instructions below and take a step towards simplifying your application's authentication flow. Happy coding!

Setup Instructions

Adding the package

If you haven't already added the packages to your web and api workspaces, we'll start off by adding the api side package. From the root directory of your RedwoodJS app, run: yarn workspace api add @spoonjoy/redwoodjs-dbauth-oauth-api

Preparing the database

First, we need to add the model for storing the linked OAuth provider information in the database. Go ahead and paste this wherever you like in api/db/schema.prisma:

model OAuth {
  provider         String
  // The ID of the user on the OAuth provider's system
  providerUserId   String
  // The username of the user on the OAuth provider's system. Useful for helping users identify their linked accounts
  providerUsername String
  // The below two fields should be in reference to whatever your user model is
  user             User          @relation(fields: [userId], references: [id])
  userId           String

  createdAt DateTime @default(now())

  @@unique([provider, providerUserId])
  @@unique([userId, provider])
  @@index([userId])
}

You'll also want to add an OAuth relation to the User model, as well as its previously required hashedPassword and salt fields optional (since users may want to only authenticate via OAuth, they'll never get to enter a password):

 model User {
   id                  Int       @id @default(autoincrement())
   email               String    @unique
-  hashedPassword      String
+  hashedPassword      String?
-  salt                String
+  salt                String?
+  OAuth               OAuth[]
   ...
 }

!!Important - it's not just the password fields that need to be optional, it's any field that isn't:

  • id
  • email (even if it's not used as username)
  • your username field

If you have any other required fields, it'll fail when trying to create a new user.

Then, run yarn rw prisma migrate dev, and give it a name like "prepare for OAuth".

That's it! Onto the next section.

Updating the api auth function

Navigate to api/src/functions/auth.ts.

Your current auth function is most likely currently just your dbAuth config. First, we need to add the following import statement:

import { OAuthHandler } from '@spoonjoy/redwoodjs-dbauth-oauth-api'

Now, go right to the bottom of the handler function. You'll see:

return await authHandler.invoke()

Change that to the following:

const oAuthHandler = new OAuthHandler(event, context, authHandler, {
  // The name of the property you'd call on `db` to access your OAuth table.
  // i.e. if your Prisma model is named `OAuth` this value would be `oAuth`, as in `db.oAuth`
  oAuthModelAccessor: 'oAuth',
  // You can instead do `enabledProviders: {}`, but I find it more clear to be explicitly not enabling these
  // We'll enable at least one of these later on, but leave it like this for now
  enabledProviders: { apple: false, github: false, google: false },
})

switch (event.path) {
  case '/auth':
    return await authHandler.invoke()
  case '/auth/oauth':
    return await oAuthHandler.invoke()
  default:
    throw new Error('Unknown auth path')
}

There's a couple things happening in that snippet.

First, we created a new instance of the OAuthHandler - this is a class that's responsible for managing the OAuth authentication process. When you create a new instance of OAuthHandler, you pass in several arguments:

  • event: This represents the incoming HTTP request. It contains information about the request, such as the URL path (event.path), HTTP method, headers, and body.
    • This is one of the parameters to the function handler
  • context: This provides information about the runtime context in which the function is executing.
    • This is one of the parameters to the function handler
  • authHandler: This is an instance of the DbAuthHandler class, which handles traditional authentication. The OAuthHandler uses this to reuse some of its functionality and configurations.
    • You might have given this variable a different name
  • config object: This contains configuration settings for the OAuthHandler. For example, oAuthModelAccessor specifies the property name to access the OAuth table in the database, and enabledProviders specifies which OAuth providers are enabled.

The OAuthHandler provides a method called invoke(), which is called every time there's an OAuth related request. It manages the communication with the OAuth provider, handles the OAuth process (like getting the authorization code, exchanging it for a token, and saving the token), and sends the appropriate response back to the client.

Then, we switched the original return statement to a switch statement. This is done to handle different types of authentication requests - it checks the event.path property, which represents the URL path of the incoming HTTP request. Depending on the URL path, it will execute different code:

  • If the URL path is /auth, it will execute authHandler.invoke(). This is the original behavior, which handles traditional authentication requests.
  • If the URL path is /auth/oauth, it will execute oAuthHandler.invoke(). This is new behavior introduced to handle OAuth requests. In other words, the switch statement allows the application to handle both traditional and OAuth authentication requests, routing them to the appropriate handler based on the URL path.

Updating the api auth library

Double check that your getCurrentUser method is selecting the user's email attribute, if it exists on your user model. For example, if you're using a field called username as your username, you might need to make a change that looks like this:

  export const getCurrentUser = async (
    session: Decoded
  ): Promise<ICurrentUser> => {
    if (!session || typeof session.id !== 'string') {
      throw new Error('Invalid session')
    }

    const user = await db.user.findUnique({
      where: { id: session.id },
      select: {
        id: true,
        username: true,
+       email: true
      },
    })
    if (!user) throw new AuthenticationError('You are not logged in.')
    else return user
  }

This is optional, but helpful as another way to associate linked accounts with your users.

All done!

And that's it! If you haven't yet set up the web side, go check out those instructions.

Next steps

Now that you've set up both your web and api sides, it's time to enable OAuth provider(s)!