Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: sergiodxa/remix-auth
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.6.0
Choose a base ref
...
head repository: sergiodxa/remix-auth
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref

Commits on Oct 24, 2023

  1. Update example imports in README (#251)

    Update README.md
    vm authored Oct 24, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    eyedol Henry Addo
    Copy the full SHA
    05ef79f View commit details

Commits on Nov 9, 2023

  1. Update README.md and Fix Errors handling section example (#256)

    * Update README.md and Fix Errors handling section example
    
    In Errors Handling section example when we read Errors from flash we must also commit the new session otherwise the error still remain in the session
    
    * Update README.md
    
    ---------
    
    Co-authored-by: Sergio Xalambrí <hello@sergiodxa.com>
    aliabdy and sergiodxa authored Nov 9, 2023
    Copy the full SHA
    93875c1 View commit details

Commits on Nov 15, 2023

  1. Allow chaining with Authenticator#use (#258)

    Update authenticator.ts
    lifeiscontent authored Nov 15, 2023
    Copy the full SHA
    7f0f4bc View commit details

Commits on Dec 3, 2023

  1. Update Types in readme code snippets (#260)

    Update README.md
    
    update readme file code snippets to use consistent types.
    shyamlohar authored Dec 3, 2023
    Copy the full SHA
    1d9ae20 View commit details

Commits on Dec 13, 2023

  1. README Remix.run docs links (#262)

    netanelben authored Dec 13, 2023
    Copy the full SHA
    5982fca View commit details

Commits on Feb 9, 2024

  1. Fix import from remix-auth-oauth2 in README (#268)

    Fix import from remix-auth-oauth2
    penx authored Feb 9, 2024
    Copy the full SHA
    610b485 View commit details

Commits on May 26, 2024

  1. Use LoaderFunctionArgs and ActionFunctionArgs instead in jsdoc (#282)

    fzn0x authored May 26, 2024
    Copy the full SHA
    b22ea79 View commit details

Commits on May 30, 2024

  1. Allow logout function to receive a HeadersInit (#271)

    * feat: headers for logout
    
    * Apply suggestions from code review
    
    ---------
    
    Co-authored-by: Sergio Xalambrí <hello@sergiodxa.com>
    reichhartd and sergiodxa authored May 30, 2024
    Copy the full SHA
    8b52021 View commit details
  2. Fix JSDoc @default for throwOnError (#264)

    Fix jsdoc for throwOnError
    mw10013 authored May 30, 2024
    Copy the full SHA
    4c659e6 View commit details
  3. 3.7.0

    sergiodxa committed May 30, 2024
    Copy the full SHA
    ab7aef1 View commit details

Commits on Jun 19, 2024

  1. Fix typo on JSDoc text (#284)

    fzn0x authored Jun 19, 2024
    Copy the full SHA
    a2287ac View commit details

Commits on Sep 20, 2024

  1. Modernize package setup (#295)

    * Modernize package setup
    
    - Use Bun instead of npm and jest
    - Use Biome instead of ESLint and Prettier
    - Add TypeDoc
    - Update TSConfig configuration
    - Remove Authorizer
    
    * Create exports.ts
    
    ---------
    
    Co-authored-by: Sergio Xalambrí <sergio@silverback.ventures>
    sergiodxa and sergiodxa-silverback authored Sep 20, 2024
    Copy the full SHA
    f1bc31a View commit details
  2. Bump uuid from 8.3.2 to 10.0.0 (#296)

    Bumps [uuid](https://github.com/uuidjs/uuid) from 8.3.2 to 10.0.0.
    - [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
    - [Commits](uuidjs/uuid@v8.3.2...v10.0.0)
    
    ---
    updated-dependencies:
    - dependency-name: uuid
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Sep 20, 2024
    Copy the full SHA
    abc28f5 View commit details
  3. Bump @arethetypeswrong/cli from 0.15.4 to 0.16.4 (#297)

    Bumps [@arethetypeswrong/cli](https://github.com/arethetypeswrong/arethetypeswrong.github.io/tree/HEAD/packages/cli) from 0.15.4 to 0.16.4.
    - [Release notes](https://github.com/arethetypeswrong/arethetypeswrong.github.io/releases)
    - [Changelog](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/packages/cli/CHANGELOG.md)
    - [Commits](https://github.com/arethetypeswrong/arethetypeswrong.github.io/commits/HEAD/packages/cli)
    
    ---
    updated-dependencies:
    - dependency-name: "@arethetypeswrong/cli"
      dependency-type: direct:development
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Sep 20, 2024
    Copy the full SHA
    3466985 View commit details
  4. Bump @types/uuid from 8.3.4 to 10.0.0 (#298)

    Bumps [@types/uuid](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/uuid) from 8.3.4 to 10.0.0.
    - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
    - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/uuid)
    
    ---
    updated-dependencies:
    - dependency-name: "@types/uuid"
      dependency-type: direct:development
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Sep 20, 2024
    Copy the full SHA
    220f939 View commit details

Commits on Oct 28, 2024

  1. Bump uuid from 10.0.0 to 11.0.2 (#304)

    Bumps [uuid](https://github.com/uuidjs/uuid) from 10.0.0 to 11.0.2.
    - [Release notes](https://github.com/uuidjs/uuid/releases)
    - [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
    - [Commits](uuidjs/uuid@v10.0.0...v11.0.2)
    
    ---
    updated-dependencies:
    - dependency-name: uuid
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Oct 28, 2024
    Copy the full SHA
    a4ea43d View commit details

Commits on Nov 11, 2024

  1. Bump @arethetypeswrong/cli from 0.16.4 to 0.17.0 (#305)

    Bumps [@arethetypeswrong/cli](https://github.com/arethetypeswrong/arethetypeswrong.github.io/tree/HEAD/packages/cli) from 0.16.4 to 0.17.0.
    - [Release notes](https://github.com/arethetypeswrong/arethetypeswrong.github.io/releases)
    - [Changelog](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/packages/cli/CHANGELOG.md)
    - [Commits](https://github.com/arethetypeswrong/arethetypeswrong.github.io/commits/HEAD/packages/cli)
    
    ---
    updated-dependencies:
    - dependency-name: "@arethetypeswrong/cli"
      dependency-type: direct:development
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Nov 11, 2024
    Copy the full SHA
    d2b211a View commit details

Commits on Nov 24, 2024

  1. Upgrade from Remix v2 to React Router v7 (#306)

    thdk authored Nov 24, 2024
    Copy the full SHA
    c67147b View commit details
  2. Use a Cookie object instead of SessionStorage (#299)

    * remove unused dependencies and upgrade other
    
    * Change from using SessionStorage to Cookie
    
    * Update documentation
    
    * Add Zod as dev dependency
    
    * Change to RR
    
    * Simplify even more by removing dependency on the Cookie
    
    Let each strategy expect it if needed
    
    * Regenerate bun.lockb
    
    * Run build before verifying exports
    
    * Drop RR requirement and suggest `@mjackson/headers` for cookies
    
    * Uninstall react-router dev dependency
    
    ---------
    
    Co-authored-by: Sergio Xalambrí <sergio@silverback.ventures>
    sergiodxa and sergiodxa-silverback authored Nov 24, 2024
    Copy the full SHA
    89ad79d View commit details
  3. 4.0.0

    sergiodxa committed Nov 24, 2024
    Copy the full SHA
    d4eb06d View commit details
  4. Include strategy file in TypeDoc

    sergiodxa committed Nov 24, 2024
    Copy the full SHA
    9ebffad View commit details

Commits on Nov 25, 2024

  1. Bump typedoc-plugin-mdn-links from 3.3.8 to 4.0.1 (#307)

    Bumps [typedoc-plugin-mdn-links](https://github.com/Gerrit0/typedoc-plugin-mdn-links) from 3.3.8 to 4.0.1.
    - [Changelog](https://github.com/Gerrit0/typedoc-plugin-mdn-links/blob/main/CHANGELOG.md)
    - [Commits](Gerrit0/typedoc-plugin-mdn-links@v3.3.8...v4.0.1)
    
    ---
    updated-dependencies:
    - dependency-name: typedoc-plugin-mdn-links
      dependency-type: direct:development
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Nov 25, 2024
    Copy the full SHA
    8dd5dc9 View commit details

Commits on Nov 28, 2024

  1. Upgrade typedoc

    sergiodxa committed Nov 28, 2024
    Copy the full SHA
    d8afa90 View commit details
  2. Add method to get an strategy instance from authenticator

    Closes #309
    sergiodxa committed Nov 28, 2024
    Copy the full SHA
    581bf6b View commit details

Commits on Dec 17, 2024

  1. Copy the full SHA
    dfe564a View commit details
  2. Update devDependencies to latest versions

    sergiodxa committed Dec 17, 2024
    Copy the full SHA
    126b60e View commit details
  3. 4.1.0

    sergiodxa committed Dec 17, 2024
    Copy the full SHA
    2e51e6f View commit details
  4. Fix type annotation for action function parameters in README

    sergiodxa committed Dec 17, 2024
    Copy the full SHA
    7b58c4c View commit details

Commits on Dec 23, 2024

  1. Bump @mjackson/headers from 0.8.0 to 0.9.0 (#314)

    Bumps [@mjackson/headers](https://github.com/mjackson/remix-the-web/tree/HEAD/packages/headers) from 0.8.0 to 0.9.0.
    - [Release notes](https://github.com/mjackson/remix-the-web/releases)
    - [Changelog](https://github.com/mjackson/remix-the-web/blob/main/packages/headers/CHANGELOG.md)
    - [Commits](https://github.com/mjackson/remix-the-web/commits/headers@0.9.0/packages/headers)
    
    ---
    updated-dependencies:
    - dependency-name: "@mjackson/headers"
      dependency-type: direct:development
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Dec 23, 2024
    Copy the full SHA
    e288f40 View commit details

Commits on Feb 3, 2025

  1. Bump @mjackson/headers from 0.9.0 to 0.10.0 (#317)

    Bumps [@mjackson/headers](https://github.com/mjackson/remix-the-web/tree/HEAD/packages/headers) from 0.9.0 to 0.10.0.
    - [Release notes](https://github.com/mjackson/remix-the-web/releases)
    - [Changelog](https://github.com/mjackson/remix-the-web/blob/main/packages/headers/CHANGELOG.md)
    - [Commits](https://github.com/mjackson/remix-the-web/commits/headers@0.10.0/packages/headers)
    
    ---
    updated-dependencies:
    - dependency-name: "@mjackson/headers"
      dependency-type: direct:development
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Feb 3, 2025
    Copy the full SHA
    c438ec9 View commit details
33 changes: 0 additions & 33 deletions .eslintrc.js

This file was deleted.

20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: 2

updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
reviewers:
- "sergiodxa"
assignees:
- "sergiodxa"

- package-ecosystem: npm
directory: /
schedule:
interval: "weekly"
reviewers:
- "sergiodxa"
assignees:
- "sergiodxa"
33 changes: 18 additions & 15 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
changelog:
categories:
- title: Documentation Changes
labels:
- documentation
- title: New Strategy
labels:
- strategy
- title: New Features
labels:
- enhancement
- title: Bug Fixes
labels:
- bug
- title: Other Changes
labels:
- "*"
- title: New Features
labels:
- enhancement
- title: Documentation Changes
labels:
- documentation
- title: Bug Fixes
labels:
- bug
- title: Example
labels:
- example
- title: Deprecations
labels:
- deprecated
- title: Other Changes
labels:
- "*"
34 changes: 16 additions & 18 deletions .github/workflows/bump.yml
Original file line number Diff line number Diff line change
@@ -4,36 +4,34 @@ on:
workflow_dispatch:
inputs:
version:
description: 'Semver type of new version (major / minor / patch)'
description: "Type of version to bump"
required: true
type: choice
options:
- major
- minor
- patch
- major
- minor
- patch

jobs:
bump-version:
name: Bump version
runs-on: ubuntu-latest
steps:
- name: Check out source
uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.DEPLOY_KEY }}
- name: Setup Node.js
uses: actions/setup-node@v2

- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile

- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
uses: bahmutov/npm-install@v1
- name: Setup Git
run: |
node-version: "lts/*"

- run: |
git config user.name 'Sergio Xalambrí'
git config user.email 'hello@sergiodxa.com'
- name: bump version
run: npm version ${{ github.event.inputs.version }}
- name: Push latest version
run: git push origin main --follow-tags
- run: npm version ${{ github.event.inputs.version }}
- run: bun run quality:fix
- run: git push origin main --follow-tags
50 changes: 50 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: CI

on: [push]

jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run build

typecheck:
name: Typechecker
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run typecheck

quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run quality

test:
name: Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun test

exports:
name: Verify Exports
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run build
- run: bun run exports
24 changes: 24 additions & 0 deletions .github/workflows/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Enable auto-merge for Dependabot PRs

on:
pull_request:
types: opened

permissions:
contents: write
pull-requests: write

jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- id: metadata
uses: dependabot/fetch-metadata@v2
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"

- run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
33 changes: 33 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Deploy Documentation

on:
push:
branches: ["main"]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: "docs"
cancel-in-progress: false

jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bunx typedoc
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: "./docs"
- uses: actions/deploy-pages@v4
id: deployment
82 changes: 0 additions & 82 deletions .github/workflows/main.yml

This file was deleted.

17 changes: 11 additions & 6 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -5,16 +5,21 @@ on:
types: [published]

jobs:
build:
publish-npm:
name: "Publish to npm"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run build
- run: bun run exports

- uses: actions/setup-node@v4
with:
node-version: 18
node-version: "lts/*"
registry-url: https://registry.npmjs.org/
- run: npm install
- run: npm run build

- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
/node_modules
/build
/coverage

*.log
.DS_Store
/node_modules
/docs
4 changes: 0 additions & 4 deletions .prettierrc.js

This file was deleted.

3 changes: 3 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["biomejs.biome"]
}
24 changes: 24 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit",
"quickfix.biome": "explicit"
}
}
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -2,15 +2,15 @@

## Setup

Create an environment variable called `REMIX_TOKEN` with your [Remix install token](https://remix.run/dashboard).
Run `bun install` to install the dependencies.

Run `npm install` to install the dependencies.
Run the tests with `bun test`.

Run the tests with `npm run test`.
Run the code quality checker with `bun run quality`.

Run the linter with `npm run lint`.
Run the typechecker with `bun run typecheck`.

Run the typechecker with `npm run typecheck`.
Run the exports checker with `bun run exports`.

## Create a Strategy

369 changes: 218 additions & 151 deletions README.md

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"$schema": "https://biomejs.dev/schemas/1.7.1/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"useHookAtTopLevel": "error"
},
"performance": {
"noBarrelFile": "error",
"noReExportAll": "error"
},
"style": {
"noDefaultExport": "error",
"noNegationElse": "error",
"useConst": "off",
"useExportType": "off",
"useImportType": "off"
},
"suspicious": {
"noConsoleLog": "warn",
"noEmptyBlockStatements": "warn",
"noSkippedTests": "error"
}
}
},
"formatter": { "enabled": true },
"vcs": {
"enabled": true,
"clientKind": "git",
"defaultBranch": "main",
"useIgnoreFile": true
},
"overrides": [
{
"include": ["**/*.md"],
"formatter": { "indentStyle": "tab" }
}
]
}
Binary file added bun.lockb
Binary file not shown.
20 changes: 0 additions & 20 deletions config/jest.config.ts

This file was deleted.

24 changes: 0 additions & 24 deletions config/jest/babel.config.js

This file was deleted.

138 changes: 0 additions & 138 deletions config/jest/setup.ts

This file was deleted.

113 changes: 0 additions & 113 deletions docs/authenticator.md

This file was deleted.

33 changes: 0 additions & 33 deletions docs/avoid-redirects.md

This file was deleted.

256 changes: 0 additions & 256 deletions docs/create-a-strategy.md

This file was deleted.

52 changes: 0 additions & 52 deletions docs/testing.md

This file was deleted.

13 changes: 0 additions & 13 deletions jest.d.ts

This file was deleted.

23,725 changes: 0 additions & 23,725 deletions package-lock.json

This file was deleted.

131 changes: 54 additions & 77 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,79 +1,56 @@
{
"name": "remix-auth",
"version": "3.6.0",
"description": "Simple Authentication for Remix",
"main": "./build/index.js",
"types": "./build/index.d.ts",
"scripts": {
"build": "tsc --project tsconfig.json",
"typecheck": "tsc --project tsconfig.json --noEmit",
"lint": "eslint --ext .ts,.tsx src/",
"test": "jest --config=config/jest.config.ts --passWithNoTests",
"coverage": "npm run test -- --coverage"
},
"keywords": [
"remix",
"auth",
"authentication",
"local",
"auth0",
"oauth2",
"strategies"
],
"author": {
"name": "Sergio Xalambrí",
"email": "hello@sergiodxa.com",
"url": "https://sergiodxa.com"
},
"repository": {
"url": "https://github.com/sergiodxa/remix-auth",
"type": "git"
},
"homepage": "https://github.com/sergiodxa/remix-auth#readme",
"license": "MIT",
"files": [
"build",
"package.json",
"README.md"
],
"peerDependencies": {
"@remix-run/react": "^1.0.0 || ^2.0.0",
"@remix-run/server-runtime": "^1.0.0 || ^2.0.0"
},
"devDependencies": {
"@babel/core": "^7.14.2",
"@babel/preset-env": "^7.14.1",
"@babel/preset-react": "^7.13.13",
"@babel/preset-typescript": "^7.13.0",
"@remix-run/node": "^2.0.1",
"@remix-run/react": "^2.0.1",
"@remix-run/serve": "^2.0.1",
"@remix-run/server-runtime": "^2.0.1",
"@types/jest": "^29.5.5",
"@types/prop-types": "^15.7.4",
"@types/react": "^18.2.20",
"@types/uuid": "^8.3.3",
"@typescript-eslint/eslint-plugin": "^6.7.3",
"@typescript-eslint/parser": "^6.7.3",
"babel-jest": "^26.6.3",
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-cypress": "^2.11.3",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.3.6",
"eslint-plugin-jest-dom": "^3.9.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-testing-library": "^4.3.0",
"eslint-plugin-unicorn": "^32.0.1",
"jest": "^29.7.0",
"jest-fetch-mock": "^3.0.3",
"prettier": "^2.3.2",
"react": "^18.2.0",
"ts-node": "^9.1.1",
"typescript": "^5.1.6"
},
"dependencies": {
"uuid": "^8.3.2"
}
"name": "remix-auth",
"version": "4.1.0",
"author": {
"name": "Sergio Xalambrí",
"email": "hello+oss@sergiodxa.com",
"url": "https://sergiodxa.com"
},
"repository": {
"url": "https://github.com/sergiodxa/remix-auth",
"type": "git"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.1",
"@biomejs/biome": "^1.8.3",
"@mjackson/headers": "^0.10.0",
"@total-typescript/tsconfig": "^1.0.4",
"@types/bun": "^1.1.14",
"msw": "^2.7.0",
"typedoc": "^0.27.5",
"typedoc-plugin-mdn-links": "^4.0.5",
"typescript": "^5.5.4"
},
"exports": {
".": "./build/index.js",
"./strategy": "./build/strategy.js",
"./package.json": "./package.json"
},
"bugs": {
"url": "https://github.com/sergiodxa/remix-auth/issues"
},
"description": "Simple Authentication for Remix and React Router",
"engines": {
"node": ">=20.0.0"
},
"files": [
"build",
"src",
"package.json",
"README.md"
],
"funding": [
"https://github.com/sponsors/sergiodxa"
],
"homepage": "https://github.com/sergiodxa/remix-auth",
"license": "MIT",
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit",
"quality": "biome check .",
"quality:fix": "biome check . --write --unsafe",
"exports": "bun run ./scripts/exports.ts"
},
"sideEffects": false,
"type": "module"
}
55 changes: 55 additions & 0 deletions scripts/exports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
async function main() {
let proc = Bun.spawn([
"bunx",
"attw",
"-f",
"table-flipped",
"--no-emoji",
"--no-color",
"--pack",
]);

let text = await new Response(proc.stdout).text();

let entrypointLines = text
.slice(text.indexOf('"remix-auth/'))
.split("\n")
.filter(Boolean)
.filter((line) => !line.includes("─"))
.map((line) =>
line
.replaceAll(/[^\d "()/A-Za-z-]/g, "")
.replaceAll("90m│39m", "│")
.replaceAll(/^/g, "")
.replaceAll(/$/g, ""),
);

let pkg = await Bun.file("package.json").json();
let entrypoints = entrypointLines.map((entrypointLine) => {
let [entrypoint, ...resolutionColumns] = entrypointLine.split("│");
if (!entrypoint) throw new Error("Entrypoint not found");
if (!resolutionColumns[2]) throw new Error("ESM resolution not found");
if (!resolutionColumns[3]) throw new Error("Bundler resolution not found");
return {
entrypoint: entrypoint.replace(pkg.name, ".").trim(),
esm: resolutionColumns[2].trim(),
bundler: resolutionColumns[3].trim(),
};
});

let entrypointsWithProblems = entrypoints.filter(
(item) => item.esm.includes("fail") || item.bundler.includes("fail"),
);

if (entrypointsWithProblems.length > 0) {
console.error("Entrypoints with problems:");
process.exit(1);
}
}

await main().catch((error) => {
console.error(error);
process.exit(1);
});

export {};
282 changes: 0 additions & 282 deletions src/authenticator.ts

This file was deleted.

103 changes: 0 additions & 103 deletions src/authorizer.ts

This file was deleted.

5 changes: 0 additions & 5 deletions src/error.ts

This file was deleted.

60 changes: 60 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { beforeEach, describe, expect, mock, test } from "bun:test";
import { Authenticator } from "./index.js";
import { Strategy } from "./strategy.js";

class MockStrategy<User> extends Strategy<User, Record<string, never>> {
name = "mock";

async authenticate() {
let user = await this.verify({});
if (user) return user;
throw new Error("Invalid credentials");
}
}

describe(Authenticator.name, () => {
beforeEach(() => mock.restore());

test("#constructor", () => {
let auth = new Authenticator();
expect(auth).toBeInstanceOf(Authenticator);
});

test("#use", () => {
let auth = new Authenticator();

expect(auth.use(new MockStrategy(async () => ({ id: 1 })))).toBe(auth);

expect(
auth.authenticate("mock", new Request("http://remix.auth/test")),
).resolves.toEqual({ id: 1 });
});

test("#unuse", () => {
let auth = new Authenticator().use(new MockStrategy(async () => null));

expect(auth.unuse("mock")).toBe(auth);

expect(
async () =>
await auth.authenticate("mock", new Request("http://remix.auth/test")),
).toThrow(new ReferenceError("Strategy mock not found."));
});

test("#authenticate", async () => {
let auth = new Authenticator().use(
new MockStrategy(async () => ({ id: 1 })),
);

expect(
await auth.authenticate("mock", new Request("http://remix.auth/test")),
).toEqual({ id: 1 });
});

test("#get", () => {
let auth = new Authenticator();
let strategy = new MockStrategy(async () => ({ id: 1 }));
expect(auth.use(strategy)).toBe(auth);
expect(auth.get("mock")).toBe(strategy);
});
});
71 changes: 67 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,67 @@
export * from "./authenticator";
export * from "./authorizer";
export * from "./error";
export * from "./strategy";
import type { Strategy } from "./strategy.js";

/**
* Create a new instance of the Authenticator.
*
* It receives a instance of a Cookie created using Remix's createCookie.
*
* It optionally receives an object with extra options. The supported options
* are:
* @example
* let auth = new Authenticator();
*/
export class Authenticator<User = unknown> {
/**
* A map of the configured strategies, the key is the name of the strategy
* @private
*/
private strategies = new Map<string, Strategy<User, never>>();

/**
* Call this method with the Strategy, the optional name allows you to setup
* the same strategy multiple times with different names.
* It returns the Authenticator instance for concatenation.
* @example
* auth.use(new SomeStrategy((user) => Promise.resolve(user)));
* auth.use(new SomeStrategy((user) => Promise.resolve(user)), "another");
*/
use(strategy: Strategy<User, never>, name?: string): Authenticator<User> {
this.strategies.set(name ?? strategy.name, strategy);
return this;
}

/**
* Call this method with the name of the strategy you want to remove.
* It returns the Authenticator instance for concatenation.
* @example
* auth.unuse("another").unuse("some");
*/
unuse(name: string): Authenticator {
this.strategies.delete(name);
return this;
}

/**
* Call this method with the name of a strategy you want to get.
* It returns the Strategy instance or null if the strategy is not found.
* @param name
* @returns
*/
get(name: string) {
return this.strategies.get(name) ?? null;
}

/**
* Call this to authenticate a request using some strategy. You pass the name
* of the strategy you want to use and the request to authenticate.
* @example
* async function action({ request }: ActionFunctionArgs) {
* let user = await auth.authenticate("strategy-name", request);
* };
*/
authenticate(strategy: string, request: Request): Promise<User> {
let instance = this.get(strategy);
if (!instance) throw new ReferenceError(`Strategy ${strategy} not found.`);
return instance.authenticate(new Request(request.url, request));
}
}
87 changes: 87 additions & 0 deletions src/strategy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { describe, expect, test } from "bun:test";
import { Cookie } from "@mjackson/headers";
import { Strategy } from "./strategy";

type User = number;
type VerifyOptions = { userId: string };

class SimpleStrategy extends Strategy<User, VerifyOptions> {
name = "mock";

public authenticate(request: Request): Promise<User> {
let url = new URL(request.url);
let userId = url.searchParams.get("userId");
if (!userId) throw new Error("Invalid credentials");
return this.verify({ userId });
}
}

describe(SimpleStrategy.name, () => {
test("#constructor", () => {
let strategy = new SimpleStrategy(async ({ userId }) => Number(userId));
expect(strategy).toBeInstanceOf(Strategy);
});

test("#authenticate (success)", async () => {
let strategy = new SimpleStrategy(async ({ userId }) => Number(userId));
let request = new Request("http://remix.auth/test?userId=1");
expect(strategy.authenticate(request)).resolves.toBe(1);
});

test("#authenticate (failure)", async () => {
let strategy = new SimpleStrategy(async ({ userId }) => Number(userId));
let request = new Request("http://remix.auth/test");
expect(() => strategy.authenticate(request)).toThrow("Invalid credentials");
});
});

class CookieStrategy extends Strategy<User, VerifyOptions> {
name = "cookie";

constructor(
protected cookieName: string,
verify: Strategy.VerifyFunction<User, VerifyOptions>,
) {
super(verify);
}

public async authenticate(request: Request): Promise<User> {
let cookie = new Cookie(request.headers.get("cookie") ?? "");
let userId = cookie.get(this.cookieName);
if (!userId) throw new Error("Invalid credentials");
return this.verify({ userId });
}
}

describe(CookieStrategy.name, () => {
test("#constructor", () => {
let strategy = new CookieStrategy("auth", async ({ userId }) =>
Number(userId),
);
expect(strategy).toBeInstanceOf(Strategy);
});

test("#authenticate (success)", async () => {
let strategy = new CookieStrategy("auth", async ({ userId }) =>
Number(userId),
);

let cookie = new Cookie();
cookie.set("auth", "1");

let request = new Request("http://remix.auth/test", {
headers: { cookie: cookie.toString() },
});

expect(strategy.authenticate(request)).resolves.toBe(1);
});

test("#authenticate (failure)", async () => {
let strategy = new CookieStrategy("auth", async ({ userId }) =>
Number(userId),
);
let request = new Request("http://remix.auth/test");

expect(() => strategy.authenticate(request)).toThrow("Invalid credentials");
});
});
195 changes: 34 additions & 161 deletions src/strategy.ts
Original file line number Diff line number Diff line change
@@ -1,171 +1,44 @@
import {
AppLoadContext,
json,
redirect,
SessionStorage,
} from "@remix-run/server-runtime";
import { AuthorizationError } from "./error";

/**
* Extra information from the Authenticator to the strategy
*/
export interface AuthenticateOptions {
/**
* The key of the session used to set the user data.
*/
sessionKey: string;
/**
* In what key of the session the errors will be set.
* @default "auth:error"
*/
sessionErrorKey: string;
/**
* The key of the session used to set the strategy used to authenticate the
* user.
*/
sessionStrategyKey: string;
/**
* The name used to register the strategy
*/
name: string;
/**
* To what URL redirect in case of a successful authentication.
* If not defined, it will return the user data.
*/
successRedirect?: string;
/**
* To what URL redirect in case of a failed authentication.
* If not defined, it will return null
*/
failureRedirect?: string;
/**
* Set if the strategy should throw an error instead of a Reponse in case of
* a failed authentication.
* @default true
*/
throwOnError?: boolean;
/**
* The context object received by the loader or action.
* This can be used by the strategy if needed.
*/
context?: AppLoadContext;
}

/**
* A function which will be called to find the user using the information the
* strategy got from the request.
*
* @param params The params from the strategy.
* @returns The user data.
* @throws {AuthorizationError} If the user was not found. Any other error will be ignored and thrown again by the strategy.
*/
export interface StrategyVerifyCallback<User, VerifyParams> {
(params: VerifyParams): Promise<User>;
}

/**
* The Strategy class is the base class every strategy should extend.
*
* This class receives two generics, a User and a VerifyParams.
* - User is the type of the user data.
* - VerifyParams is the type of the params the verify callback will receive from the strategy.
*
* This class also defines as protected two methods, `success` and `failure`.
* - `success` is called when the authentication was successful.
* - `failure` is called when the authentication failed.
* These methods helps you return or throw the correct value, response or error
* from within the strategy `authenticate` method.
*/
export abstract class Strategy<User, VerifyOptions> {
/**
* The name of the strategy.
* This will be used by the Authenticator to identify and retrieve the
* strategy.
*/
public abstract name: string;

public constructor(
protected verify: StrategyVerifyCallback<User, VerifyOptions>
) {}

/**
* The authentication flow of the strategy.
*
* This method receives the Request to authenticator and the session storage
* to use from the Authenticator. It may receive a custom callback.
*
* At the end of the flow, it will return a Response to be used by the
* application.
*/
public abstract authenticate(
request: Request,
sessionStorage: SessionStorage,
options: AuthenticateOptions
): Promise<User>;

/**
* Throw an AuthorizationError or a redirect to the failureRedirect.
* @param message The error message to set in the session.
* @param request The request to get the cookie out of.
* @param sessionStorage The session storage to retrieve the session from.
* @param options The strategy options.
* @throws {AuthorizationError} If the throwOnError is set to true.
* @throws {Response} If the failureRedirect is set or throwOnError is false.
* @returns {Promise<never>}
*/
protected async failure(
message: string,
request: Request,
sessionStorage: SessionStorage,
options: AuthenticateOptions,
cause?: Error
): Promise<never> {
// if a failureRedirect is not set, we throw a 401 Response or an error
if (!options.failureRedirect) {
if (options.throwOnError) throw new AuthorizationError(message, cause);
throw json<{ message: string }>({ message }, 401);
}

let session = await sessionStorage.getSession(
request.headers.get("Cookie")
);

// if we do have a failureRedirect, we redirect to it and set the error
// in the session errorKey
session.flash(options.sessionErrorKey, { message });
throw redirect(options.failureRedirect, {
headers: { "Set-Cookie": await sessionStorage.commitSession(session) },
});
}

/**
* Returns the user data or throw a redirect to the successRedirect.
* @param user The user data to set in the session.
* @param request The request to get the cookie out of.
* @param sessionStorage The session storage to retrieve the session from.
* @param options The strategy options.
* @returns {Promise<User>} The user data.
* @throws {Response} If the successRedirect is set, it will redirect to it.
*/
protected async success(
user: User,
request: Request,
sessionStorage: SessionStorage,
options: AuthenticateOptions
): Promise<User> {
// if a successRedirect is not set, we return the user
if (!options.successRedirect) return user;

let session = await sessionStorage.getSession(
request.headers.get("Cookie")
);
/**
* The name of the strategy.
* This will be used by the Authenticator to identify and retrieve the
* strategy.
*/
public abstract name: string;

public constructor(
protected verify: Strategy.VerifyFunction<User, VerifyOptions>,
) {}

/**
* The authentication flow of the strategy.
*
* This method receives the Request from the authenticator we want to
* authenticate.
*
* At the end of the flow, it will return a the User data to be used by the
* application.
*/
public abstract authenticate(request: Request): Promise<User>;
}

// if we do have a successRedirect, we redirect to it and set the user
// in the session sessionKey
session.set(options.sessionKey, user);
session.set(options.sessionStrategyKey, options.name ?? this.name);
throw redirect(options.successRedirect, {
headers: { "Set-Cookie": await sessionStorage.commitSession(session) },
});
}
export namespace Strategy {
/**
* A function which will be called to find the user using the information the
* strategy got from the request.
*
* @param params The params from the strategy.
* @returns The user data.
* @throws {AuthorizationError} If the user was not found. Any other error will be ignored and thrown again by the strategy.
*/
export type VerifyFunction<User, VerifyParams> = (
params: VerifyParams,
) => Promise<User>;
}
239 changes: 0 additions & 239 deletions test/authenticator.test.ts

This file was deleted.

152 changes: 0 additions & 152 deletions test/authorizers.test.ts

This file was deleted.

21 changes: 7 additions & 14 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
{
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"esModuleInterop": true,
"moduleResolution": "Node",
"module": "CommonJS",
"target": "ES2019",
"strict": true,
"skipLibCheck": true,
"declaration": true,
"jsx": "react-jsx",
"outDir": "./build"
},
"exclude": ["node_modules"],
"include": ["src/**/*.ts", "src/**/*.tsx", "jest.d.ts"]
/* Change to `@total-typescript/tsconfig/tsc/dom/library` for DOM usage */
"extends": "@total-typescript/tsconfig/tsc/dom/library",
"include": ["src/**/*"],
"exclude": ["src/**/*.test.*"],
"compilerOptions": {
"outDir": "./build"
}
}
10 changes: 10 additions & 0 deletions typedoc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "https://typedoc.org/schema.json",
"includeVersion": true,
"entryPoints": ["./src/index.ts", "./src/strategy.ts"],
"out": "docs",
"json": "docs/index.json",
"cleanOutputDir": true,
"plugin": ["typedoc-plugin-mdn-links"],
"categorizeByGroup": false
}