-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
12,977 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# EditorConfig is awesome: http://EditorConfig.org | ||
|
||
# top-most EditorConfig file | ||
root = true | ||
|
||
# Unix-style newlines with a newline ending every file | ||
[*] | ||
charset = utf-8 | ||
end_of_line = lf | ||
indent_size = 2 | ||
indent_style = space | ||
insert_final_newline = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"extends": [ | ||
"tailored-tunes", | ||
"plugin:json/recommended", | ||
"plugin:security/recommended" | ||
], | ||
"root": true, | ||
"parser": "@typescript-eslint/parser", | ||
"parserOptions": { | ||
"project": "./tsconfig.json" | ||
}, | ||
"plugins": [ | ||
"json", | ||
"@typescript-eslint" | ||
], | ||
"rules": { | ||
"no-console": "off", | ||
"no-else-return": "off", | ||
"no-unused-expressions": "off", | ||
"max-nested-callbacks": ["error", 4] | ||
}, | ||
"env": { | ||
"commonjs": false, | ||
"es6": true, | ||
"node": true | ||
}, | ||
"ignorePatterns": [ | ||
"**/*.d.ts" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
{ | ||
"$schema": "https://docs.renovatebot.com/renovate-schema.json", | ||
"extends": [ | ||
"config:base" | ||
], | ||
"enabledManagers": [ | ||
"npm" | ||
], | ||
"baseBranches": [ | ||
"main" | ||
], | ||
"semanticCommits": "enabled", | ||
"semanticCommitType": "chore", | ||
"packageRules": [ | ||
{ | ||
"matchDepTypes": [ | ||
"devDependencies" | ||
], | ||
"matchUpdateTypes": [ | ||
"minor", | ||
"patch", | ||
"pin", | ||
"digest" | ||
], | ||
"automerge": true, | ||
"semanticCommitType": "chore" | ||
}, | ||
{ | ||
"matchDepTypes": [ | ||
"dependencies" | ||
], | ||
"matchUpdateTypes": [ | ||
"patch" | ||
], | ||
"automerge": true, | ||
"semanticCommitType": "chore" | ||
}, | ||
{ | ||
"matchDepTypes": [ | ||
"dependencies" | ||
], | ||
"matchUpdateTypes": [ | ||
"minor" | ||
], | ||
"automerge": true, | ||
"semanticCommitType": "fix" | ||
} | ||
], | ||
"schedule": [ | ||
"after 10pm and before 5am every weekday", | ||
"every weekend" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
name: Verify | ||
|
||
on: | ||
push: | ||
branches: [ main, alpha, beta ] | ||
|
||
env: | ||
LEFTHOOK: 0 | ||
|
||
jobs: | ||
verify: | ||
uses: meza/shared-github-workflows/.github/workflows/default-node-npm-ci.yml@main | ||
secrets: | ||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
with: | ||
test: false | ||
node-version: "latest" | ||
junit-report-path: "reports/junit.xml" | ||
cobertura-report-path: "reports/coverage/cobertura-coverage.xml" | ||
build: | ||
runs-on: ubuntu-latest | ||
needs: [ verify ] | ||
if: needs.verify.outputs.new-release-published == 'true' | ||
name: Build | ||
steps: | ||
- name: ⏬ Checkout | ||
uses: actions/checkout@v3 | ||
- name: 🔧 Set up node | ||
uses: meza/action-setup-node-npm@main | ||
with: | ||
node-version: latest | ||
cache-name: ${{ needs.verify.outputs.cache-name }} | ||
- name: 🔢 Set version | ||
run: npm version --no-git-tag-version ${{ needs.verify.outputs.new-release-version }} | ||
- name: 🔨 Build | ||
run: npm run build | ||
- name: 🚀 Release | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
run: npm run release |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.idea | ||
dist | ||
node_modules | ||
reports | ||
.cache | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"branches": [ | ||
"+([0-9])?(.{+([0-9]),x}).x", | ||
"main", | ||
"next", | ||
{ | ||
"name": "beta", | ||
"prerelease": true | ||
}, | ||
{ | ||
"name": "alpha", | ||
"prerelease": true | ||
} | ||
], | ||
"plugins": [ | ||
"@semantic-release/commit-analyzer", | ||
"semantic-release-export-data", | ||
"@semantic-release/release-notes-generator", | ||
"@semantic-release/npm", | ||
[ | ||
"@semantic-release/github" | ||
] | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,186 @@ | ||
# auth0-remix-server | ||
An Auth0 library for server side Remix | ||
<div style="text-align: center;"> | ||
<h1>Auth0 Remix Server</h1> | ||
<p> | ||
The missing library for authentication on the server with Remix | ||
</p> | ||
<p> | ||
<i>Please contribute!</i> | ||
</p> | ||
</div> | ||
|
||
--- | ||
|
||
## What is this? | ||
|
||
This is a library for authentication with [Auth0](https://auth0.com/) on the server with Remix. | ||
|
||
It's built as part of the efforts to deliver the [trance-stack](https://github.com/meza/trance-stack/). As such, the initial | ||
release of this library only covers the MVP needs of the stack. It will keep evolving over time and with your help. | ||
|
||
## What is still missing? | ||
|
||
- [ ] utilise the STATE parameter to prevent CSRF | ||
- [ ] failed events should remove the user from the session automatically | ||
- [ ] see if we can handle the callback while maintaining the session from before the login | ||
- [ ] create the callbacks for the id token and the refresh tokens | ||
- [ ] opt out of the session handling | ||
- [ ] enable register with passing ?screen_hint=signup to the authorize endpoint | ||
|
||
## How to use | ||
|
||
Everything below assumes that you're using [Remix](https://remix.run/) and [Auth0](https://auth0.com/) and you're familiar | ||
with how they work. | ||
|
||
### Installation | ||
|
||
```bash | ||
npm install @meza/auth0-remix-server | ||
``` | ||
|
||
### Usage | ||
|
||
Some steps below might be familiar to anyone who attempted this with the [remix-auth-auth0](https://github.com/danestves/remix-auth-auth0/) package. | ||
|
||
> #### Environment Variables | ||
> Environment variables are not required for the library, the examples only use them when configuring the authenticator. | ||
> I do recommend using environment variables for sensitive information like client secrets and domain names. | ||
> | ||
> `AUTH0_DOMAIN` - The domain name of your Auth0 tenant | ||
> `AUTH0_CLIENT_ID` - The client ID of your Auth0 application | ||
> `AUTH0_CLIENT_SECRET` - The client secret of your Auth0 application | ||
> `APP_DOMAIN` - The domain name of your application (http://localhost:3333 for local development) | ||
#### 1. Create an instance of the authenticator in `src/auth.server.ts` | ||
|
||
```ts | ||
// src/auth.server.ts | ||
import { Auth0RemixServer } from '@meza/auth0-remix-server'; | ||
import { getSessionStorage } from './sessionStorage.server'; // this is where your session storage is configured | ||
|
||
export const authenticator = new Auth0RemixServer({ | ||
clientDetails: { | ||
domain: process.env.AUTH0_DOMAIN, | ||
clientID: process.env.AUTH0_CLIENT_ID, | ||
clientSecret: process.env.AUTH0_CLIENT_SECRET | ||
}, | ||
callbackURL: `${process.env.APP_DOMAIN}/auth/callback`, | ||
refreshTokenRotationEnabled: true, | ||
failedLoginRedirect: '/', | ||
session: { | ||
store: getSessionStorage() | ||
} | ||
}); | ||
``` | ||
|
||
#### 2. Create a login route in `src/routes/login.tsx` | ||
|
||
```tsx | ||
// src/routes/login.tsx | ||
import { redirect } from '@remix-run/node'; | ||
|
||
export default () => { | ||
return ( | ||
<Form action="/auth/auth0" method="post"> | ||
<button>Login</button> | ||
</Form> | ||
); | ||
}; | ||
``` | ||
|
||
#### 3. Create an authentication route in `src/routes/auth/auth0.ts` | ||
|
||
```tsx | ||
// src/routes/auth/auth0.ts | ||
import { authenticator } from '../../auth.server'; | ||
import type { ActionFunction } from '@remix-run/node'; | ||
|
||
export const action: ActionFunction = () => { | ||
const forceLogin = false; // set to true to force auth0 to ask for a login | ||
authenticator.authorize(forceLogin); | ||
}; | ||
``` | ||
|
||
#### 4. Create a callback route in `src/routes/auth/callback.tsx` | ||
|
||
```tsx | ||
// src/routes/auth/callback.tsx | ||
import { authenticator } from '../../auth.server'; | ||
import type { ActionFunction } from '@remix-run/node'; | ||
|
||
export const action: ActionFunction = async ({ request }) => { | ||
await authenticator.handleCallback(request, { | ||
onSuccessRedirect: '/dashboard' // change this to be wherever you want to redirect to after a successful login | ||
}); | ||
}; | ||
``` | ||
|
||
#### 5. Create a logout route in `src/routes/logout.tsx` | ||
|
||
```tsx | ||
import { authenticator } from '../auth.server'; | ||
import { destroySession, getSessionFromRequest } from '../session.server'; | ||
import type { ActionFunction } from '@remix-run/node'; | ||
|
||
export const action: ActionFunction = async ({ request }) => { | ||
const session = await getSessionFromRequest(request); | ||
|
||
await authenticator.logout(process.env.APP_DOMAIN, { | ||
'Set-Cookie': await destroySession(session) // this is where you destroy the session | ||
}); | ||
}; | ||
|
||
export const loader = action; // this to allow you to hit /logout directly in the browser | ||
``` | ||
|
||
#### 6. Optional - Create a dashboard route in `src/routes/dashboard.tsx` | ||
|
||
```tsx | ||
import { json } from '@remix-run/node'; | ||
import { Form, useLoaderData } from '@remix-run/react'; | ||
import { authenticator } from '../auth.server'; | ||
import type { LoaderFunction } from '@remix-run/node'; | ||
|
||
export const loader: LoaderFunction = async ({ request, context }) => { | ||
const user = await authenticator.getUser(request, context); // this is what determines if the user is logged in or not | ||
return json({ | ||
user: user | ||
}); | ||
}; | ||
|
||
export default () => { | ||
const { user } = useLoaderData<typeof loader>(); | ||
return ( | ||
<div> | ||
<div>Dashboard for {user.nickname || user.givenName || user.name}</div> | ||
<Form action="/logout" method="post"> | ||
<button>Logout</button> | ||
</Form> | ||
</div> | ||
); | ||
}; | ||
|
||
|
||
``` | ||
|
||
## Gotchas | ||
|
||
### Refresh Token Rotation | ||
|
||
The `refreshTokenRotationEnabled` option is set to `false` by default. This is because it's off by default in Auth0. | ||
|
||
When it's set to `true`, the refresh tokens will be appended to the session. This is secure and makes it easier to manage | ||
the refresh tokens. | ||
|
||
Please see [this post](https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation) and [this one](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/#Refresh-Token-Rotation) | ||
for more information. | ||
|
||
### Refreshing the access token | ||
|
||
Until [this issue](https://github.com/remix-run/react-router/issues/9566) in Remix is shipped, you'll need to pass in | ||
the context from the loaders and actions to the `getUser` method. | ||
|
||
This ensures (in an awkward way) that the refresh only happens once. | ||
|
||
It's not pretty but once we have proper middleware in Remix, it should clean up. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Lefthook | ||
# | ||
# Skip lefthook execution: | ||
# e.g. `LEFTHOOK=0 git commit -am "Lefthook skipped"` | ||
# https://github.com/evilmartians/lefthook/blob/master/docs/full_guide.md#skip-lefthook-execution | ||
# | ||
# | ||
# Full Lefthook guide: | ||
# https://github.com/evilmartians/lefthook/blob/master/docs/full_guide.md | ||
# | ||
# Full list of git hooks: | ||
# https://git-scm.com/docs/githooks | ||
|
||
commit-msg: | ||
commands: | ||
lint-commit-msg: | ||
run: npx commitlint --edit | ||
pre-commit: | ||
parallel: true | ||
commands: | ||
lint: | ||
run: npm run lint | ||
test: | ||
run: npm run report | ||
post-merge: | ||
commands: | ||
install-deps-postmerge: | ||
run: npm install |
Oops, something went wrong.