Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @redwoodjs/auth package #497

Merged
merged 27 commits into from
May 9, 2020
Merged

Add @redwoodjs/auth package #497

merged 27 commits into from
May 9, 2020

Conversation

peterp
Copy link
Contributor

@peterp peterp commented May 2, 2020

Web side

The first step is to pick which authentication library you want to use: Netlify Identity, GoTrue, or Auth0 are currently supported.

As an example I'll pick Netlify Identity, and wrap the RedwoodProvider in our AuthProvider.

// web/index.js
import netlifyIdentity from 'netlify-identity-widget'
netlifyIdentity.init()

ReactDOM.render(
  <FatalErrorBoundary>
    <AuthProvider client={netlifyIdentity} type="netlify">
      <RedwoodProvider>
        <Routes />
      </RedwoodProvider>
    </AuthProvider>
  </FatalErrorBoundary>,
  document.getElementById('redwood-app')
)

Redwood's auth package offers a hook based API:

const UserAuthTools = () => {
  const { loading, authenticated, login, logout } = useAuth()

  if (loading) {
    return 'loading...'
  }

  return (
    <Button
      onClick={async () => {
        if (authenticated) {
          await logout({ redirectTo: 'http://localhost:8910/' })
          navigate('/')
        } else {
          await login()
          navigate(routes.invoice())
        }
      }}
    >
      {authenticated ? 'Log out' : 'Log in'}
    </Button>
  )
}

Protecting Routes: We're introducing a new PrivateRoute component that gate keeps unauthenticated users.

// routes.js

import { Router, Route, PrivateRoute } from '@redwoodjs/router'

const Routes = () => {
  return (
    <Router>
      <Route unauthorized path="/" page={LandingPage} name="home" />
      <PrivateRoute path="/invoice" page={InvoicePage} name="invoice" />
    </Router>
  )
}

API side

The currentUser is automatically added to the context of each request in the GraphQL server.

  • Copy @redwoodjs/auth from example-invoice.
  • Route when you're not authenticated.
  • Decode jwt when using Netlify identity during development.
  • Documentation.

#214

@peterp peterp marked this pull request as ready for review May 2, 2020 22:29
@peterp peterp changed the title Add way to grab user from auth headers. Add @redwoodjs/auth package May 3, 2020
context: NetlifyClientContext
}) => {
const type = event?.headers[REDWOOD_AUTH_TYPE_HEADER] as SupportedAuthTypes
switch (type) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If using an enum above for SupportAuthTypes, could use something like https://github.com/UselessPickles/ts-enum-util here


export interface AuthClient {
restoreAuthState?(): void | Promise<any>
login(options?: any): Promise<any>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add TODO to add options typing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually had some trouble here and would love to hear how you would solve this:

Auth0 does not take arguments for the login function, but Netlify does. I thought I could solve this with function overloading, but I realise that this is probably not for functions associated to an object?

So I added this random "options" argument that could be anything, and added a better type definition in AuthClientNetlify

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you're saying. Even if you mark options as optional with a type, that still implies the Auth0 version would accept the options arg.

You could do something like this:

export interface AuthClient {
  restoreAuthState?(): void | Promise<any>
  login(): Promise<any>
  logout(): void | Promise<void>
}

export type AuthClientAuth0 = AuthClient

export interface AuthClientNetlify extends Omit<AuthClient, 'login'> {
  login(options: {
    email: string
    password: string
    remember?: boolean
  }): Promise<NetlifyUser>
  client: Netlify
}

...only problem there is that Netlify Auth Clients will need to do something like
(client as AuthClientNetlify).login(args)

but it's possible a consumer would've already cast it anyway

client: SupportedAuthClients,
type: SupportedAuthTypes
): AuthClient => {
switch (type) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment on enum versus types as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @jmreidy; I had a look at this and I wanted these to be enums that are backed as string, because I the auth type as a header value, and I couldn't really find a good motivation for converting them to enums.

Is there something that I haven't really thought about?

"outDir": "dist",
"rootDir": "src",
// gotrue-js does not return types for `removeSavedSession` and `recoverSession`
"noImplicitAny": false,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boo. Could we extend with a .d.ts module? Alternatively I'd be happy to contribute by adding those types upstream to gotrue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is my first real TS project and I had some trouble figuring out what to do in this case - it seemed like contributing upstream was the recommended way, but hadn't considered fixing it via a .d.ts file.

I'll give that a go!

Copy link

@jmreidy jmreidy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope I'm not jumping the gun on providing feedback. This is awesome work, and it's so exciting to see it in TS! Thanks so much for driving this @peterp ! 💪

@peterp
Copy link
Contributor Author

peterp commented May 5, 2020

@jmreidy this is totally helpful, thank you!

@peterp peterp merged commit 54333f9 into master May 9, 2020
@thedavidprice thedavidprice added this to the next release milestone May 10, 2020
@thedavidprice thedavidprice deleted the pp-add-auth branch June 9, 2020 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants