├── .eslintrc.js
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── Bug Report.yml
│ ├── Feature Request.yml
│ └── config.yml
├── actions
│ ├── build
│ │ └── action.yml
│ ├── get-prerelease
│ │ └── action.yml
│ ├── get-release-notes
│ │ └── action.yml
│ ├── get-version
│ │ └── action.yml
│ ├── npm-publish
│ │ └── action.yml
│ ├── release-create
│ │ └── action.yml
│ ├── rl-scanner
│ │ └── action.yml
│ └── tag-exists
│ │ └── action.yml
├── dependabot.yml
├── stale.yml
└── workflows
│ ├── browserstack.yml
│ ├── codeql.yml
│ ├── integration.yml
│ ├── npm-release.yml
│ ├── release.yml
│ ├── rl-secure.yml
│ ├── semgrep.yml
│ ├── snyk.yml
│ └── test.yml
├── .gitignore
├── .husky
└── pre-commit
├── .prettierignore
├── .prettierrc
├── .semgrepignore
├── .shiprc
├── .version
├── CHANGELOG.md
├── CONTRIBUTING.md
├── EXAMPLES.md
├── FAQ.md
├── Jenkinsfile
├── LICENSE
├── MIGRATION_GUIDE.md
├── README.md
├── TROUBLESHOOTING.md
├── __mocks__
└── @auth0
│ └── auth0-spa-js.tsx
├── __tests__
├── auth-provider.test.tsx
├── auth-reducer.test.tsx
├── errors.test.tsx
├── helpers.tsx
├── ssr.test.tsx
├── use-auth.test.tsx
├── utils.test.tsx
├── with-auth0.test.tsx
└── with-authentication-required.test.tsx
├── browserstack.json
├── codecov.yml
├── cypress-bs.config.js
├── cypress.config.js
├── cypress
├── e2e
│ ├── smoke-bs.cy.ts
│ └── smoke.cy.ts
└── tsconfig.json
├── docs
├── .nojekyll
├── assets
│ ├── highlight.css
│ ├── icons.js
│ ├── icons.svg
│ ├── main.js
│ ├── navigation.js
│ ├── search.js
│ └── style.css
├── classes
│ ├── AuthenticationError.html
│ ├── GenericError.html
│ ├── InMemoryCache.html
│ ├── LocalStorageCache.html
│ ├── MfaRequiredError.html
│ ├── MissingRefreshTokenError.html
│ ├── OAuthError.html
│ ├── PopupCancelledError.html
│ ├── PopupTimeoutError.html
│ ├── TimeoutError.html
│ └── User.html
├── functions
│ ├── Auth0Context.html
│ ├── Auth0Provider.html
│ ├── useAuth0.html
│ ├── withAuth0.html
│ └── withAuthenticationRequired.html
├── hierarchy.html
├── index.html
├── interfaces
│ ├── Auth0ContextInterface.html
│ ├── Auth0ProviderOptions.html
│ ├── AuthorizationParams.html
│ ├── GetTokenSilentlyOptions.html
│ ├── GetTokenWithPopupOptions.html
│ ├── ICache.html
│ ├── IdToken.html
│ ├── LogoutOptions.html
│ ├── LogoutUrlOptions.html
│ ├── PopupConfigOptions.html
│ ├── PopupLoginOptions.html
│ ├── RedirectLoginOptions.html
│ ├── WithAuth0Props.html
│ └── WithAuthenticationRequiredOptions.html
├── modules.html
└── types
│ ├── AppState.html
│ ├── CacheLocation.html
│ └── Cacheable.html
├── examples
├── README.md
├── cra-react-router
│ ├── .env.sample
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ ├── src
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── Error.tsx
│ │ ├── Loading.tsx
│ │ ├── Nav.tsx
│ │ ├── Users.tsx
│ │ ├── index.tsx
│ │ ├── react-app-env.d.ts
│ │ └── use-api.ts
│ └── tsconfig.json
├── gatsby-app
│ ├── .env.development.sample
│ ├── .eslintrc
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .prettierrc
│ ├── README.md
│ ├── gatsby-browser.js
│ ├── gatsby-config.js
│ ├── package.json
│ └── src
│ │ ├── components
│ │ ├── App.css
│ │ ├── Error.js
│ │ ├── Loading.js
│ │ ├── Nav.js
│ │ └── Users.js
│ │ ├── hooks
│ │ └── use-api.js
│ │ └── pages
│ │ ├── index.js
│ │ └── users.js
├── nextjs-app
│ ├── .env.sample
│ ├── .gitignore
│ ├── README.md
│ ├── components
│ │ ├── App.css
│ │ ├── Error.js
│ │ ├── Loading.js
│ │ └── Nav.js
│ ├── hooks
│ │ └── use-api.js
│ ├── package.json
│ └── pages
│ │ ├── _app.js
│ │ ├── _document.js
│ │ ├── index.js
│ │ └── users.js
└── users-api
│ ├── .env.sample
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ └── server.js
├── jest.config.js
├── opslevel.yml
├── package-lock.json
├── package.json
├── rollup.config.mjs
├── scripts
└── oidc-provider.mjs
├── src
├── auth-state.tsx
├── auth0-context.tsx
├── auth0-provider.tsx
├── errors.tsx
├── index.tsx
├── reducer.tsx
├── use-auth0.tsx
├── utils.tsx
├── with-auth0.tsx
└── with-authentication-required.tsx
├── static
└── index.html
├── tsconfig.json
└── typedoc.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | ecmaVersion: 2020,
5 | sourceType: 'module',
6 | ecmaFeatures: {
7 | jsx: true,
8 | },
9 | },
10 | settings: {
11 | react: {
12 | version: 'detect',
13 | },
14 | },
15 | extends: [
16 | 'plugin:react/recommended',
17 | 'plugin:@typescript-eslint/recommended',
18 | 'plugin:react-hooks/recommended',
19 | ],
20 | rules: {
21 | '@typescript-eslint/camelcase': 'off',
22 | },
23 | ignorePatterns: ['examples/**'],
24 | overrides: [
25 | {
26 | files: ['*.js'],
27 | rules: {
28 | '@typescript-eslint/no-var-requires': 'off',
29 | },
30 | },
31 | ],
32 | };
33 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @auth0/project-dx-sdks-engineer-codeowner
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug Report.yml:
--------------------------------------------------------------------------------
1 | name: 🐞 Report a bug
2 | description: Have you found a bug or issue? Create a bug report for this library
3 | labels: ["bug"]
4 |
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | **Please do not report security vulnerabilities here**. The [Responsible Disclosure Program](https://auth0.com/responsible-disclosure-policy) details the procedure for disclosing security issues.
10 |
11 | - type: checkboxes
12 | id: checklist
13 | attributes:
14 | label: Checklist
15 | options:
16 | - label: The issue can be reproduced in the [auth0-react sample app](https://github.com/auth0-samples/auth0-react-samples/tree/master/Sample-01) (or N/A).
17 | required: true
18 | - label: I have looked into the [Readme](https://github.com/auth0/auth0-react#readme), [Examples](https://github.com/auth0/auth0-react/blob/main/EXAMPLES.md), and [FAQ](https://github.com/auth0/auth0-react/blob/main/FAQ.md) and have not found a suitable solution or answer.
19 | required: true
20 | - label: I have looked into the [API documentation](https://auth0.github.io/auth0-react/) and have not found a suitable solution or answer.
21 | required: true
22 | - label: I have searched the [issues](https://github.com/auth0/auth0-react/issues) and have not found a suitable solution or answer.
23 | required: true
24 | - label: I have searched the [Auth0 Community](https://community.auth0.com) forums and have not found a suitable solution or answer.
25 | required: true
26 | - label: I agree to the terms within the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md).
27 | required: true
28 |
29 | - type: textarea
30 | id: description
31 | attributes:
32 | label: Description
33 | description: Provide a clear and concise description of the issue, including what you expected to happen.
34 | validations:
35 | required: true
36 |
37 | - type: textarea
38 | id: reproduction
39 | attributes:
40 | label: Reproduction
41 | description: Detail the steps taken to reproduce this error, and whether this issue can be reproduced consistently or if it is intermittent.
42 | placeholder: |
43 | 1. Step 1...
44 | 2. Step 2...
45 | 3. ...
46 | validations:
47 | required: true
48 |
49 | - type: textarea
50 | id: additional-context
51 | attributes:
52 | label: Additional context
53 | description: Other libraries that might be involved, or any other relevant information you think would be useful.
54 | validations:
55 | required: false
56 |
57 | - type: input
58 | id: environment-version
59 | attributes:
60 | label: auth0-react version
61 | validations:
62 | required: true
63 |
64 | - type: input
65 | id: environment-react-version
66 | attributes:
67 | label: React version
68 | validations:
69 | required: true
70 |
71 | - type: dropdown
72 | id: environment-browser
73 | attributes:
74 | label: Which browsers have you tested in?
75 | multiple: true
76 | options:
77 | - Chrome
78 | - Edge
79 | - Safari
80 | - Firefox
81 | - Opera
82 | - Other
83 | validations:
84 | required: true
85 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature Request.yml:
--------------------------------------------------------------------------------
1 | name: 🧩 Feature request
2 | description: Suggest an idea or a feature for this library
3 | labels: ["feature request"]
4 |
5 | body:
6 | - type: checkboxes
7 | id: checklist
8 | attributes:
9 | label: Checklist
10 | options:
11 | - label: I have looked into the [Readme](https://github.com/auth0/auth0-react#readme), [Examples](https://github.com/auth0/auth0-react/blob/main/EXAMPLES.md), and [FAQ](https://github.com/auth0/auth0-react/blob/main/FAQ.md) and have not found a suitable solution or answer.
12 | required: true
13 | - label: I have looked into the [API documentation](https://auth0.github.io/auth0-react/) and have not found a suitable solution or answer.
14 | required: true
15 | - label: I have searched the [issues](https://github.com/auth0/auth0-react/issues) and have not found a suitable solution or answer.
16 | required: true
17 | - label: I have searched the [Auth0 Community](https://community.auth0.com) forums and have not found a suitable solution or answer.
18 | required: true
19 | - label: I agree to the terms within the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md).
20 | required: true
21 |
22 | - type: textarea
23 | id: description
24 | attributes:
25 | label: Describe the problem you'd like to have solved
26 | description: A clear and concise description of what the problem is.
27 | placeholder: I'm always frustrated when...
28 | validations:
29 | required: true
30 |
31 | - type: textarea
32 | id: ideal-solution
33 | attributes:
34 | label: Describe the ideal solution
35 | description: A clear and concise description of what you want to happen.
36 | validations:
37 | required: true
38 |
39 | - type: textarea
40 | id: alternatives-and-workarounds
41 | attributes:
42 | label: Alternatives and current workarounds
43 | description: A clear and concise description of any alternatives you've considered or any workarounds that are currently in place.
44 | validations:
45 | required: false
46 |
47 | - type: textarea
48 | id: additional-context
49 | attributes:
50 | label: Additional context
51 | description: Add any other context or screenshots about the feature request here.
52 | validations:
53 | required: false
54 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Auth0 Community
4 | url: https://community.auth0.com
5 | about: Discuss this SDK in the Auth0 Community forums
6 | - name: FAQ
7 | url: https://github.com/auth0/auth0-react/blob/main/FAQ.md
8 | about: Read the FAQ to get answers to common issues
9 | - name: SDK API Documentation
10 | url: https://auth0.github.io/auth0-react/
11 | about: Read the API documentation for this SDK
12 | - name: Library Documentation
13 | url: https://auth0.com/docs/libraries/auth0-react
14 | about: Read the library docs on Auth0.com
15 |
--------------------------------------------------------------------------------
/.github/actions/build/action.yml:
--------------------------------------------------------------------------------
1 | name: Build package
2 | description: Build the SDK package
3 |
4 | inputs:
5 | node:
6 | description: The Node version to use
7 | required: false
8 | default: 18
9 |
10 | runs:
11 | using: composite
12 |
13 | steps:
14 | - name: Setup Node
15 | uses: actions/setup-node@v3
16 | with:
17 | node-version: ${{ inputs.node }}
18 | cache: 'npm'
19 |
20 | - name: Install dependencies
21 | shell: bash
22 | run: npm ci --include=dev
23 |
24 | - name: Build package
25 | shell: bash
26 | run: npm run build
27 |
--------------------------------------------------------------------------------
/.github/actions/get-prerelease/action.yml:
--------------------------------------------------------------------------------
1 | name: Return a boolean indicating if the version contains prerelease identifiers
2 |
3 | #
4 | # Returns a simple true/false boolean indicating whether the version indicates it's a prerelease or not.
5 | #
6 | # TODO: Remove once the common repo is public.
7 | #
8 |
9 | inputs:
10 | version:
11 | required: true
12 |
13 | outputs:
14 | prerelease:
15 | value: ${{ steps.get_prerelease.outputs.PRERELEASE }}
16 |
17 | runs:
18 | using: composite
19 |
20 | steps:
21 | - id: get_prerelease
22 | shell: bash
23 | run: |
24 | if [[ "${VERSION}" == *"beta"* || "${VERSION}" == *"alpha"* ]]; then
25 | echo "PRERELEASE=true" >> $GITHUB_OUTPUT
26 | else
27 | echo "PRERELEASE=false" >> $GITHUB_OUTPUT
28 | fi
29 | env:
30 | VERSION: ${{ inputs.version }}
31 |
--------------------------------------------------------------------------------
/.github/actions/get-release-notes/action.yml:
--------------------------------------------------------------------------------
1 | name: Return the release notes extracted from the body of the PR associated with the release.
2 |
3 | #
4 | # Returns the release notes from the content of a pull request linked to a release branch. It expects the branch name to be in the format release/vX.Y.Z, release/X.Y.Z, release/vX.Y.Z-beta.N. etc.
5 | #
6 | # TODO: Remove once the common repo is public.
7 | #
8 | inputs:
9 | version:
10 | required: true
11 | repo_name:
12 | required: false
13 | repo_owner:
14 | required: true
15 | token:
16 | required: true
17 |
18 | outputs:
19 | release-notes:
20 | value: ${{ steps.get_release_notes.outputs.RELEASE_NOTES }}
21 |
22 | runs:
23 | using: composite
24 |
25 | steps:
26 | - uses: actions/github-script@v7
27 | id: get_release_notes
28 | with:
29 | result-encoding: string
30 | script: |
31 | const { data: pulls } = await github.rest.pulls.list({
32 | owner: process.env.REPO_OWNER,
33 | repo: process.env.REPO_NAME,
34 | state: 'all',
35 | head: `${process.env.REPO_OWNER}:release/${process.env.VERSION}`,
36 | });
37 | core.setOutput('RELEASE_NOTES', pulls[0].body);
38 | env:
39 | GITHUB_TOKEN: ${{ inputs.token }}
40 | REPO_OWNER: ${{ inputs.repo_owner }}
41 | REPO_NAME: ${{ inputs.repo_name }}
42 | VERSION: ${{ inputs.version }}
--------------------------------------------------------------------------------
/.github/actions/get-version/action.yml:
--------------------------------------------------------------------------------
1 | name: Return the version extracted from the branch name
2 |
3 | #
4 | # Returns the version from the .version file.
5 | #
6 | # TODO: Remove once the common repo is public.
7 | #
8 |
9 | outputs:
10 | version:
11 | value: ${{ steps.get_version.outputs.VERSION }}
12 |
13 | runs:
14 | using: composite
15 |
16 | steps:
17 | - id: get_version
18 | shell: bash
19 | run: |
20 | VERSION=$(head -1 .version)
21 | echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
22 |
--------------------------------------------------------------------------------
/.github/actions/npm-publish/action.yml:
--------------------------------------------------------------------------------
1 | name: Publish release to npm
2 |
3 | inputs:
4 | node-version:
5 | required: true
6 | npm-token:
7 | required: true
8 | version:
9 | required: true
10 | require-build:
11 | default: true
12 | release-directory:
13 | default: './'
14 |
15 | runs:
16 | using: composite
17 |
18 | steps:
19 | - name: Checkout code
20 | uses: actions/checkout@v4
21 |
22 | - name: Setup Node
23 | uses: actions/setup-node@v4
24 | with:
25 | node-version: ${{ inputs.node-version }}
26 | cache: 'npm'
27 | registry-url: 'https://registry.npmjs.org'
28 |
29 | - name: Install dependencies
30 | shell: bash
31 | run: npm ci --include=dev
32 |
33 | - name: Build package
34 | if: inputs.require-build == 'true'
35 | shell: bash
36 | run: npm run build
37 |
38 | - name: Publish release to NPM
39 | shell: bash
40 | working-directory: ${{ inputs.release-directory }}
41 | run: |
42 | if [[ "${VERSION}" == *"beta"* ]]; then
43 | TAG="beta"
44 | elif [[ "${VERSION}" == *"alpha"* ]]; then
45 | TAG="alpha"
46 | else
47 | TAG="latest"
48 | fi
49 | npm publish --provenance --tag $TAG
50 | env:
51 | NODE_AUTH_TOKEN: ${{ inputs.npm-token }}
52 | VERSION: ${{ inputs.version }}
--------------------------------------------------------------------------------
/.github/actions/release-create/action.yml:
--------------------------------------------------------------------------------
1 | name: Create a GitHub release
2 |
3 | #
4 | # Creates a GitHub release with the given version.
5 | #
6 | # TODO: Remove once the common repo is public.
7 | #
8 |
9 | inputs:
10 | token:
11 | required: true
12 | files:
13 | required: false
14 | name:
15 | required: true
16 | body:
17 | required: true
18 | tag:
19 | required: true
20 | commit:
21 | required: true
22 | draft:
23 | default: false
24 | required: false
25 | prerelease:
26 | default: false
27 | required: false
28 | fail_on_unmatched_files:
29 | default: true
30 | required: false
31 |
32 | runs:
33 | using: composite
34 |
35 | steps:
36 | - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844
37 | with:
38 | body: ${{ inputs.body }}
39 | name: ${{ inputs.name }}
40 | tag_name: ${{ inputs.tag }}
41 | target_commitish: ${{ inputs.commit }}
42 | draft: ${{ inputs.draft }}
43 | prerelease: ${{ inputs.prerelease }}
44 | fail_on_unmatched_files: ${{ inputs.fail_on_unmatched_files }}
45 | files: ${{ inputs.files }}
46 | env:
47 | GITHUB_TOKEN: ${{ inputs.token }}
48 |
--------------------------------------------------------------------------------
/.github/actions/rl-scanner/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Reversing Labs Scanner'
2 | description: 'Runs the Reversing Labs scanner on a specified artifact.'
3 | inputs:
4 | artifact-path:
5 | description: 'Path to the artifact to be scanned.'
6 | required: true
7 | version:
8 | description: 'Version of the artifact.'
9 | required: true
10 |
11 | runs:
12 | using: 'composite'
13 | steps:
14 | - name: Set up Python
15 | uses: actions/setup-python@v4
16 | with:
17 | python-version: '3.10'
18 |
19 | - name: Install Python dependencies
20 | shell: bash
21 | run: |
22 | pip install boto3 requests
23 |
24 | - name: Configure AWS credentials
25 | uses: aws-actions/configure-aws-credentials@v1
26 | with:
27 | role-to-assume: ${{ env.PRODSEC_TOOLS_ARN }}
28 | aws-region: us-east-1
29 | mask-aws-account-id: true
30 |
31 | - name: Install RL Wrapper
32 | shell: bash
33 | run: |
34 | pip install rl-wrapper>=1.0.0 --index-url "https://${{ env.PRODSEC_TOOLS_USER }}:${{ env.PRODSEC_TOOLS_TOKEN }}@a0us.jfrog.io/artifactory/api/pypi/python-local/simple"
35 |
36 | - name: Run RL Scanner
37 | shell: bash
38 | env:
39 | RLSECURE_LICENSE: ${{ env.RLSECURE_LICENSE }}
40 | RLSECURE_SITE_KEY: ${{ env.RLSECURE_SITE_KEY }}
41 | SIGNAL_HANDLER_TOKEN: ${{ env.SIGNAL_HANDLER_TOKEN }}
42 | PYTHONUNBUFFERED: 1
43 | run: |
44 | if [ ! -f "${{ inputs.artifact-path }}" ]; then
45 | echo "Artifact not found: ${{ inputs.artifact-path }}"
46 | exit 1
47 | fi
48 |
49 | rl-wrapper \
50 | --artifact "${{ inputs.artifact-path }}" \
51 | --name "${{ github.event.repository.name }}" \
52 | --version "${{ inputs.version }}" \
53 | --repository "${{ github.repository }}" \
54 | --commit "${{ github.sha }}" \
55 | --build-env "github_actions" \
56 | --suppress_output
57 |
58 | # Check the outcome of the scanner
59 | if [ $? -ne 0 ]; then
60 | echo "RL Scanner failed."
61 | echo "scan-status=failed" >> $GITHUB_ENV
62 | exit 1
63 | else
64 | echo "RL Scanner passed."
65 | echo "scan-status=success" >> $GITHUB_ENV
66 | fi
67 |
68 | outputs:
69 | scan-status:
70 | description: 'The outcome of the scan process.'
71 | value: ${{ env.scan-status }}
72 |
--------------------------------------------------------------------------------
/.github/actions/tag-exists/action.yml:
--------------------------------------------------------------------------------
1 | name: Return a boolean indicating if a tag already exists for the repository
2 |
3 | #
4 | # Returns a simple true/false boolean indicating whether the tag exists or not.
5 | #
6 | # TODO: Remove once the common repo is public.
7 | #
8 |
9 | inputs:
10 | token:
11 | required: true
12 | tag:
13 | required: true
14 |
15 | outputs:
16 | exists:
17 | description: 'Whether the tag exists or not'
18 | value: ${{ steps.tag-exists.outputs.EXISTS }}
19 |
20 | runs:
21 | using: composite
22 |
23 | steps:
24 | - id: tag-exists
25 | shell: bash
26 | run: |
27 | GET_API_URL="https://api.github.com/repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG_NAME}"
28 | http_status_code=$(curl -LI $GET_API_URL -o /dev/null -w '%{http_code}\n' -s -H "Authorization: token ${GITHUB_TOKEN}")
29 | if [ "$http_status_code" -ne "404" ] ; then
30 | echo "EXISTS=true" >> $GITHUB_OUTPUT
31 | else
32 | echo "EXISTS=false" >> $GITHUB_OUTPUT
33 | fi
34 | env:
35 | TAG_NAME: ${{ inputs.tag }}
36 | GITHUB_TOKEN: ${{ inputs.token }}
37 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: 'github-actions'
4 | directory: '/'
5 | schedule:
6 | interval: 'daily'
7 | - package-ecosystem: 'npm'
8 | directory: '/'
9 | schedule:
10 | interval: 'daily'
11 | ignore:
12 | - dependency-name: "*"
13 | update-types: ["version-update:semver-major"]
14 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-stale - https://github.com/probot/stale
2 |
3 | # Number of days of inactivity before an Issue or Pull Request becomes stale
4 | daysUntilStale: 30
5 |
6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
7 | daysUntilClose: 7
8 |
9 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
10 | onlyLabels:
11 | - 'waiting for customer'
12 |
13 | # Ignore issues in projects
14 | exemptProjects: true
15 |
16 | # Ignore issues and PRs in milestones
17 | exemptMilestones: true
18 |
19 | # Set to true to ignore issues with an assignee
20 | exemptAssignees: true
21 |
22 | # Label to use when marking as stale
23 | staleLabel: closed:stale
24 |
25 | # Comment to post when marking as stale. Set to `false` to disable
26 | markComment: >
27 | This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇♂️
28 |
--------------------------------------------------------------------------------
/.github/workflows/browserstack.yml:
--------------------------------------------------------------------------------
1 | name: Browserstack
2 |
3 | on:
4 | merge_group:
5 | workflow_dispatch:
6 | pull_request:
7 | types:
8 | - opened
9 | - synchronize
10 | push:
11 | branches:
12 | - main
13 |
14 | permissions:
15 | contents: read
16 |
17 | concurrency:
18 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
19 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
20 |
21 | env:
22 | NODE_VERSION: 18
23 |
24 | jobs:
25 |
26 | browserstack:
27 |
28 | name: BrowserStack Tests
29 | runs-on: ubuntu-latest
30 |
31 | steps:
32 | - name: Checkout code
33 | uses: actions/checkout@v4
34 | with:
35 | ref: ${{ github.event.pull_request.head.sha || github.ref }}
36 |
37 | - name: Setup Node
38 | uses: actions/setup-node@v4
39 | with:
40 | node-version: ${{ env.NODE_VERSION }}
41 | cache: npm
42 |
43 | - name: Build package
44 | uses: ./.github/actions/build
45 | with:
46 | node: ${{ env.NODE_VERSION }}
47 |
48 | - name: Run tests
49 | shell: bash
50 | run: npx concurrently --raw --kill-others --success first "npm:start" "wait-on http://127.0.0.1:3000/ && browserstack-cypress run --build-name ${{ github.event.pull_request.head.sha || github.ref }} --no-wrap --specs "cypress/e2e/smoke-bs.cy.ts""
51 | env:
52 | BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
53 | BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
54 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: CodeQL
2 |
3 | on:
4 | merge_group:
5 | pull_request:
6 | types:
7 | - opened
8 | - synchronize
9 | push:
10 | branches:
11 | - main
12 | - beta
13 | schedule:
14 | - cron: '37 10 * * 2'
15 |
16 | permissions:
17 | actions: read
18 | contents: read
19 | security-events: write
20 |
21 | concurrency:
22 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
23 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
24 |
25 | jobs:
26 | analyze:
27 | name: Check for Vulnerabilities
28 | runs-on: ubuntu-latest
29 |
30 | strategy:
31 | fail-fast: false
32 | matrix:
33 | language: [javascript]
34 |
35 | steps:
36 | - if: github.actor == 'dependabot[bot]' || github.event_name == 'merge_group'
37 | run: exit 0 # Skip unnecessary test runs for dependabot and merge queues. Artifically flag as successful, as this is a required check for branch protection.
38 |
39 | - name: Checkout
40 | uses: actions/checkout@v4
41 |
42 | - name: Initialize CodeQL
43 | uses: github/codeql-action/init@v3
44 | with:
45 | languages: ${{ matrix.language }}
46 | queries: +security-and-quality
47 |
48 | - name: Autobuild
49 | uses: github/codeql-action/autobuild@v3
50 |
51 | - name: Perform CodeQL Analysis
52 | uses: github/codeql-action/analyze@v3
53 | with:
54 | category: '/language:${{ matrix.language }}'
55 |
--------------------------------------------------------------------------------
/.github/workflows/integration.yml:
--------------------------------------------------------------------------------
1 | name: Integration Tests
2 |
3 | on:
4 | merge_group:
5 | workflow_dispatch:
6 | pull_request:
7 | types:
8 | - opened
9 | - synchronize
10 | push:
11 | branches:
12 | - main
13 |
14 | permissions:
15 | contents: read
16 |
17 | concurrency:
18 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
19 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
20 |
21 | env:
22 | NODE_VERSION: 18
23 |
24 | jobs:
25 | test-examples:
26 | name: Run example tests
27 | runs-on: ubuntu-latest
28 |
29 | env:
30 | SKIP_PREFLIGHT_CHECK: true
31 | CYPRESS_USER_EMAIL: ${{secrets.CYPRESS_USER_EMAIL}}
32 | CYPRESS_USER_PASSWORD: ${{secrets.CYPRESS_USER_PASSWORD}}
33 | REACT_APP_DOMAIN: ${{secrets.TEST_DOMAIN}}
34 | REACT_APP_CLIENT_ID: ${{secrets.TEST_CLIENT_ID}}
35 | REACT_APP_AUDIENCE: ${{secrets.TEST_AUDIENCE}}
36 | GATSBY_DOMAIN: ${{secrets.TEST_DOMAIN}}
37 | GATSBY_CLIENT_ID: ${{secrets.TEST_CLIENT_ID}}
38 | GATSBY_AUDIENCE: ${{secrets.TEST_AUDIENCE}}
39 | NEXT_PUBLIC_DOMAIN: ${{secrets.TEST_DOMAIN}}
40 | NEXT_PUBLIC_CLIENT_ID: ${{secrets.TEST_CLIENT_ID}}
41 | NEXT_PUBLIC_AUDIENCE: ${{secrets.TEST_AUDIENCE}}
42 | DOMAIN: ${{secrets.TEST_DOMAIN}}
43 | AUDIENCE: ${{secrets.TEST_AUDIENCE}}
44 |
45 | steps:
46 | - name: Checkout code
47 | uses: actions/checkout@v4
48 | with:
49 | ref: ${{ github.event.pull_request.head.sha || github.ref }}
50 |
51 | - name: Install dependencies
52 | run: npm ci
53 |
54 | - name: Build SDK
55 | run: npm run build
56 |
57 | - name: Install examples
58 | run: npm run install:examples
59 |
60 | - name: Run integration test (CRA)
61 | run: npm run test:cra
62 |
63 | - name: Run integration test (NextJS)
64 | run: npm run test:nextjs
65 |
66 | - name: Run integration test (Gatsby)
67 | run: npm run test:gatsby
68 |
--------------------------------------------------------------------------------
/.github/workflows/npm-release.yml:
--------------------------------------------------------------------------------
1 | name: Create npm and GitHub Release
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | node-version:
7 | required: true
8 | type: string
9 | require-build:
10 | default: "true"
11 | type: string
12 | release-directory:
13 | default: './'
14 | type: string
15 | secrets:
16 | github-token:
17 | required: true
18 | npm-token:
19 | required: true
20 |
21 | jobs:
22 | release:
23 | if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.merged && startsWith(github.event.pull_request.head.ref, 'release/'))
24 | runs-on: ubuntu-latest
25 | environment: release
26 |
27 | steps:
28 | # Checkout the code
29 | - uses: actions/checkout@v4
30 | with:
31 | fetch-depth: 0
32 |
33 | # Get the version from the branch name
34 | - id: get_version
35 | uses: ./.github/actions/get-version
36 |
37 | # Get the prerelease flag from the branch name
38 | - id: get_prerelease
39 | uses: ./.github/actions/get-prerelease
40 | with:
41 | version: ${{ steps.get_version.outputs.version }}
42 |
43 | # Get the release notes
44 | - id: get_release_notes
45 | uses: ./.github/actions/get-release-notes
46 | with:
47 | token: ${{ secrets.github-token }}
48 | version: ${{ steps.get_version.outputs.version }}
49 | repo_owner: ${{ github.repository_owner }}
50 | repo_name: ${{ github.event.repository.name }}
51 |
52 | # Check if the tag already exists
53 | - id: tag_exists
54 | uses: ./.github/actions/tag-exists
55 | with:
56 | tag: ${{ steps.get_version.outputs.version }}
57 | token: ${{ secrets.github-token }}
58 |
59 | # If the tag already exists, exit with an error
60 | - if: steps.tag_exists.outputs.exists == 'true'
61 | run: exit 1
62 |
63 | # Publish the release to our package manager
64 | - uses: ./.github/actions/npm-publish
65 | with:
66 | node-version: ${{ inputs.node-version }}
67 | require-build: ${{ inputs.require-build }}
68 | version: ${{ steps.get_version.outputs.version }}
69 | npm-token: ${{ secrets.npm-token }}
70 | release-directory: ${{ inputs.release-directory }}
71 |
72 | # Create a release for the tag
73 | - uses: ./.github/actions/release-create
74 | with:
75 | token: ${{ secrets.github-token }}
76 | name: ${{ steps.get_version.outputs.version }}
77 | body: ${{ steps.get_release_notes.outputs.release-notes }}
78 | tag: ${{ steps.get_version.outputs.version }}
79 | commit: ${{ github.sha }}
80 | prerelease: ${{ steps.get_prerelease.outputs.prerelease }}
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Create npm and GitHub Release
2 |
3 | on:
4 | pull_request:
5 | types:
6 | - closed
7 | workflow_dispatch:
8 | permissions:
9 | contents: write
10 | id-token: write # For publishing to npm using --provenance
11 |
12 | ### TODO: Replace instances of './.github/workflows/' w/ `auth0/dx-sdk-actions/workflows/` and append `@latest` after the common `dx-sdk-actions` repo is made public.
13 | ### TODO: Also remove `get-prerelease`, `get-release-notes`, `get-version`, `npm-publish`, `release-create`, and `tag-exists` actions from this repo's .github/actions folder once the repo is public.
14 | ### TODO: Also remove `npm-release` workflow from this repo's .github/workflows folder once the repo is public.
15 |
16 | jobs:
17 | rl-scanner:
18 | uses: ./.github/workflows/rl-secure.yml
19 | with:
20 | node-version: 18
21 | artifact-name: 'auth0-react.tgz'
22 | secrets:
23 | RLSECURE_LICENSE: ${{ secrets.RLSECURE_LICENSE }}
24 | RLSECURE_SITE_KEY: ${{ secrets.RLSECURE_SITE_KEY }}
25 | SIGNAL_HANDLER_TOKEN: ${{ secrets.SIGNAL_HANDLER_TOKEN }}
26 | PRODSEC_TOOLS_USER: ${{ secrets.PRODSEC_TOOLS_USER }}
27 | PRODSEC_TOOLS_TOKEN: ${{ secrets.PRODSEC_TOOLS_TOKEN }}
28 | PRODSEC_TOOLS_ARN: ${{ secrets.PRODSEC_TOOLS_ARN }}
29 | release:
30 | uses: ./.github/workflows/npm-release.yml
31 | needs: rl-scanner
32 | with:
33 | node-version: 18
34 | require-build: true
35 | secrets:
36 | npm-token: ${{ secrets.NPM_TOKEN }}
37 | github-token: ${{ secrets.GITHUB_TOKEN }}
38 |
--------------------------------------------------------------------------------
/.github/workflows/rl-secure.yml:
--------------------------------------------------------------------------------
1 | name: RL-Secure Workflow
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | node-version:
7 | required: true
8 | type: string
9 | artifact-name:
10 | required: true
11 | type: string
12 | secrets:
13 | RLSECURE_LICENSE:
14 | required: true
15 | RLSECURE_SITE_KEY:
16 | required: true
17 | SIGNAL_HANDLER_TOKEN:
18 | required: true
19 | PRODSEC_TOOLS_USER:
20 | required: true
21 | PRODSEC_TOOLS_TOKEN:
22 | required: true
23 | PRODSEC_TOOLS_ARN:
24 | required: true
25 |
26 | jobs:
27 | rl-scanner:
28 | name: Run Reversing Labs Scanner
29 | if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.merged && startsWith(github.event.pull_request.head.ref, 'release/'))
30 | runs-on: ubuntu-latest
31 | outputs:
32 | scan-status: ${{ steps.rl-scan-conclusion.outcome }}
33 |
34 | steps:
35 | - name: Checkout code
36 | uses: actions/checkout@v4
37 | with:
38 | fetch-depth: 0
39 |
40 | - name: Build package
41 | uses: ./.github/actions/build
42 | with:
43 | node: ${{ inputs.node-version }}
44 |
45 | - name: Create tgz build artifact
46 | run: |
47 | tar -czvf ${{ inputs.artifact-name }} *
48 |
49 | - id: get_version
50 | uses: ./.github/actions/get-version
51 |
52 | - name: Run RL Scanner
53 | id: rl-scan-conclusion
54 | uses: ./.github/actions/rl-scanner
55 | with:
56 | artifact-path: "$(pwd)/${{ inputs.artifact-name }}"
57 | version: "${{ steps.get_version.outputs.version }}"
58 | env:
59 | RLSECURE_LICENSE: ${{ secrets.RLSECURE_LICENSE }}
60 | RLSECURE_SITE_KEY: ${{ secrets.RLSECURE_SITE_KEY }}
61 | SIGNAL_HANDLER_TOKEN: ${{ secrets.SIGNAL_HANDLER_TOKEN }}
62 | PRODSEC_TOOLS_USER: ${{ secrets.PRODSEC_TOOLS_USER }}
63 | PRODSEC_TOOLS_TOKEN: ${{ secrets.PRODSEC_TOOLS_TOKEN }}
64 | PRODSEC_TOOLS_ARN: ${{ secrets.PRODSEC_TOOLS_ARN }}
65 |
66 | - name: Output scan result
67 | run: echo "scan-status=${{ steps.rl-scan-conclusion.outcome }}" >> $GITHUB_ENV
68 |
--------------------------------------------------------------------------------
/.github/workflows/semgrep.yml:
--------------------------------------------------------------------------------
1 | name: Semgrep
2 |
3 | on:
4 | merge_group:
5 | pull_request:
6 | types:
7 | - opened
8 | - synchronize
9 | push:
10 | branches:
11 | - main
12 | - beta
13 | schedule:
14 | - cron: '30 0 1,15 * *'
15 |
16 | permissions:
17 | contents: read
18 |
19 | concurrency:
20 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
21 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
22 |
23 | jobs:
24 | run:
25 | name: Check for Vulnerabilities
26 | runs-on: ubuntu-latest
27 |
28 | container:
29 | image: returntocorp/semgrep
30 |
31 | steps:
32 | - if: github.actor == 'dependabot[bot]' || github.event_name == 'merge_group'
33 | run: exit 0 # Skip unnecessary test runs for dependabot and merge queues. Artifically flag as successful, as this is a required check for branch protection.
34 |
35 | - uses: actions/checkout@v4
36 | with:
37 | ref: ${{ github.event.pull_request.head.sha || github.ref }}
38 |
39 | - run: semgrep ci
40 | env:
41 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
42 |
--------------------------------------------------------------------------------
/.github/workflows/snyk.yml:
--------------------------------------------------------------------------------
1 | name: Snyk
2 |
3 | on:
4 | merge_group:
5 | workflow_dispatch:
6 | pull_request:
7 | types:
8 | - opened
9 | - synchronize
10 | push:
11 | branches:
12 | - main
13 | - beta
14 | schedule:
15 | - cron: '30 0 1,15 * *'
16 |
17 | permissions:
18 | contents: read
19 |
20 | concurrency:
21 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
22 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
23 |
24 | jobs:
25 | check:
26 |
27 | name: Check for Vulnerabilities
28 | runs-on: ubuntu-latest
29 |
30 | steps:
31 | - if: github.actor == 'dependabot[bot]' || github.event_name == 'merge_group'
32 | run: exit 0 # Skip unnecessary test runs for dependabot and merge queues. Artifically flag as successful, as this is a required check for branch protection.
33 |
34 | - uses: actions/checkout@v4
35 | with:
36 | ref: ${{ github.event.pull_request.head.sha || github.ref }}
37 |
38 | - uses: snyk/actions/node@b98d498629f1c368650224d6d212bf7dfa89e4bf # pin@0.4.0
39 | env:
40 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
41 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Unit Tests
2 |
3 | on:
4 | merge_group:
5 | workflow_dispatch:
6 | pull_request:
7 | branches:
8 | - main
9 | push:
10 | branches:
11 | - main
12 |
13 | permissions:
14 | contents: read
15 |
16 | concurrency:
17 | group: ${{ github.workflow }}-${{ github.ref }}
18 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
19 |
20 | env:
21 | NODE_VERSION: 18
22 | CACHE_KEY: '${{ github.ref }}-${{ github.run_id }}-${{ github.run_attempt }}'
23 |
24 | jobs:
25 | build:
26 | name: Build Package
27 | runs-on: ubuntu-latest
28 |
29 | steps:
30 | - name: Checkout code
31 | uses: actions/checkout@v4
32 | with:
33 | ref: ${{ github.event.pull_request.head.sha || github.ref }}
34 |
35 | - name: Build package
36 | uses: ./.github/actions/build
37 | with:
38 | node: ${{ env.NODE_VERSION }}
39 |
40 | - name: Save build artifacts
41 | uses: actions/cache/save@v4
42 | with:
43 | path: .
44 | key: ${{ env.CACHE_KEY }}
45 |
46 | unit:
47 | needs: build # Require build to complete before running tests
48 |
49 | name: Run Tests
50 | runs-on: ubuntu-latest
51 |
52 | steps:
53 | - name: Checkout code
54 | uses: actions/checkout@v4
55 |
56 | - name: Setup Node
57 | uses: actions/setup-node@v4
58 | with:
59 | node-version: ${{ env.NODE_VERSION }}
60 | cache: npm
61 |
62 | - name: Restore build artifacts
63 | uses: actions/cache/restore@v4
64 | with:
65 | path: .
66 | key: ${{ env.CACHE_KEY }}
67 |
68 | - name: Run tests
69 | run: npm run test
70 |
71 | - name: Upload coverage
72 | uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 # pin@3.1.5
73 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | .idea
107 | test-results
108 |
109 | cypress/screenshots
110 | cypress/videos
111 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx pretty-quick --staged
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | CHANGELOG.md
2 | docs
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "printWidth": 80
4 | }
5 |
--------------------------------------------------------------------------------
/.semgrepignore:
--------------------------------------------------------------------------------
1 | .circleci/
2 | .github/
3 | __mocks__/
4 | __tests__/
5 | cypress/
6 | docs/
7 | examples/
8 | static/
9 | README.md
10 |
--------------------------------------------------------------------------------
/.shiprc:
--------------------------------------------------------------------------------
1 | {
2 | "files": {
3 | ".version": []
4 | },
5 | "postbump": "npm run docs"
6 | }
--------------------------------------------------------------------------------
/.version:
--------------------------------------------------------------------------------
1 | v2.3.0
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We appreciate feedback and contribution to this repo! Before you get started, please see [Auth0's general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
4 |
5 | ## Local development
6 |
7 | Install the dependencies and start the development server:
8 |
9 | ```bash
10 | npm install
11 | npm start
12 | ```
13 |
14 | This will run a development server at http://localhost:3000 with a simple application that demonstrates the main features of the SDK. When you make changes the development server will live reload.
15 |
16 | You can change the default Auth0 tenant and application by editing the domain and clientId in [static/index.html](./static/index.html#L81-L82)
17 |
18 | ## Running the examples
19 |
20 | The examples are React applications and an Express API. To run the example apps see the instructions in [examples/README.md](./examples/README.md)
21 |
22 | ## Running the unit tests
23 |
24 | The unit tests use Jest and are run with:
25 |
26 | ```bash
27 | npm test
28 | ```
29 |
30 | ## Running the integration tests
31 |
32 | The integration tests run against the examples, so you must follow the instructions to set up the examples in [examples/README.md](./examples/README.md) first.
33 |
34 | Then run:
35 |
36 | ```bash
37 | CYPRESS_USER_EMAIL={YOUR USER} CYPRESS_USER_PASSWORD={YOUR PW} npm run test:integration
38 | ```
39 |
40 | `CYPRESS_USER_EMAIL` and `CYPRESS_USER_PASSWORD` should be the credentials of a user on your Auth0 tenant that has the `read:users` permissions on the audience you specified when setting up the examples.
41 |
--------------------------------------------------------------------------------
/FAQ.md:
--------------------------------------------------------------------------------
1 | # Frequently Asked Questions
2 |
3 | **Note:** `auth0-react` uses [Auth0 SPA JS](https://github.com/auth0/auth0-spa-js) behind the scenes, so be sure to check [their FAQs](https://github.com/auth0/auth0-spa-js/blob/main/FAQ.md) too.
4 |
5 | 1. [User is not logged in after page refresh](#1-user-is-not-logged-in-after-page-refresh)
6 | 2. [User is not logged in after successful sign in with redirect](#2-user-is-not-logged-in-after-successful-sign-in-with-redirect)
7 |
8 | ## 1. User is not logged in after page refresh
9 |
10 | There are usually 2 reasons for this:
11 |
12 | **1. The user logged in with a Social Provider (like Google) and you are using the Auth0 Developer Keys**
13 |
14 | If you are using the [Classic Universal Login](https://auth0.com/docs/universal-login/classic) experience, [Silent Authentication](https://auth0.com/docs/authorization/configure-silent-authentication) won't work on the `/authorize` endpoint. This library uses Silent Authentication internally to check if a user is already signed in after page refresh, so that won't work either. You should either change to the [New Universal Login](https://auth0.com/docs/universal-login/new-experience) experience or [add your own keys](https://auth0.com/docs/connections/identity-providers-social) to that particular social connection.
15 |
16 | **2. You are using a browser like Safari or Brave that has Intelligent Tracking Prevention turned on by default**
17 |
18 | In this case Silent Authentication will not work because it relies on a hidden iframe being logged in to a different domain (usually `auth0.com`) and browsers with ITP do not allow third-party (eg iframed) cookies. There are 2 workarounds for this using [Rotating Refresh Tokens](https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation) or [Custom Domains](https://auth0.com/docs/custom-domains)
19 |
20 | ## 2. User is not logged in after successful sign in with redirect
21 |
22 | If after successfully logging in, your user returns to your SPA and is still not authenticated, do _not_ refresh the page - go to the Network tab on Chrome and confirm that the POST to `oauth/token` resulted in an error `401 Unauthorized`. If this is the case, your tenant is most likely misconfigured. Go to your **Application Properties** in your application's settings in the [Auth0 Dashboard](https://manage.auth0.com) and make sure that `Application Type` is set to `Single Page Application` and `Token Endpoint Authentication Method` is set to `None` (**Note:** there is a known issue with the Auth0 "Default App", if you are unable to set `Token Endpoint Authentication Method` to `None`, create a new Application of type `Single Page Application` or see the advice in [issues/93](https://github.com/auth0/auth0-react/issues/93#issuecomment-673431605))
23 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | @Library('k8sAgents') agentLibrary
2 | @Library('auth0') _
3 | SDKDeployment()
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Auth0
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://www.npmjs.com/package/@auth0/auth0-react)
4 | [](https://codecov.io/gh/auth0/auth0-react)
5 | 
6 | [](https://opensource.org/licenses/MIT)
7 | [](https://circleci.com/gh/auth0/auth0-react)
8 |
9 | 📚 [Documentation](#documentation) - 🚀 [Getting Started](#getting-started) - 💻 [API Reference](#api-reference) - 💬 [Feedback](#feedback)
10 |
11 | ## Documentation
12 |
13 | - [Quickstart](https://auth0.com/docs/quickstart/spa/react) - our interactive guide for quickly adding login, logout and user information to a React app using Auth0.
14 | - [Sample App](https://github.com/auth0-samples/auth0-react-samples/tree/master/Sample-01) - a full-fledged React application integrated with Auth0.
15 | - [FAQs](https://github.com/auth0/auth0-react/blob/main/FAQ.md) - frequently asked questions about the auth0-react SDK.
16 | - [Examples](https://github.com/auth0/auth0-react/blob/main/EXAMPLES.md) - code samples for common React authentication scenario's.
17 | - [Docs site](https://www.auth0.com/docs) - explore our docs site and learn more about Auth0.
18 |
19 | ## Getting started
20 |
21 | ### Installation
22 |
23 | Using [npm](https://npmjs.org/)
24 |
25 | ```bash
26 | npm install @auth0/auth0-react
27 | ```
28 |
29 | Using [yarn](https://yarnpkg.com/)
30 |
31 | ```bash
32 | yarn add @auth0/auth0-react
33 | ```
34 |
35 | ### Configure Auth0
36 |
37 | Create a **Single Page Application** in the [Auth0 Dashboard](https://manage.auth0.com/#/applications).
38 |
39 | > **If you're using an existing application**, verify that you have configured the following settings in your Single Page Application:
40 | >
41 | > - Click on the "Settings" tab of your application's page.
42 | > - Scroll down and click on the "Show Advanced Settings" link.
43 | > - Under "Advanced Settings", click on the "OAuth" tab.
44 | > - Ensure that "JsonWebToken Signature Algorithm" is set to `RS256` and that "OIDC Conformant" is enabled.
45 |
46 | Next, configure the following URLs for your application under the "Application URIs" section of the "Settings" page:
47 |
48 | - **Allowed Callback URLs**: `http://localhost:3000`
49 | - **Allowed Logout URLs**: `http://localhost:3000`
50 | - **Allowed Web Origins**: `http://localhost:3000`
51 |
52 | > These URLs should reflect the origins that your application is running on. **Allowed Callback URLs** may also include a path, depending on where you're handling the callback.
53 |
54 | Take note of the **Client ID** and **Domain** values under the "Basic Information" section. You'll need these values in the next step.
55 |
56 | ### Configure the SDK
57 |
58 | Configure the SDK by wrapping your application in `Auth0Provider`:
59 |
60 | ```jsx
61 | // src/index.js
62 | import React from 'react';
63 | import { createRoot } from 'react-dom/client';
64 | import { Auth0Provider } from '@auth0/auth0-react';
65 | import App from './App';
66 |
67 | const root = createRoot(document.getElementById('app'));
68 |
69 | root.render(
70 |
77 |
78 |
79 | );
80 | ```
81 |
82 |
83 | Instructions for React <18
84 |
85 |
86 | ```jsx
87 | // src/index.js
88 | import React from 'react';
89 | import ReactDOM from 'react-dom';
90 | import { Auth0Provider } from '@auth0/auth0-react';
91 | import App from './App';
92 |
93 | ReactDOM.render(
94 |
101 |
102 | ,
103 | document.getElementById('app')
104 | );
105 | ```
106 |
107 |
108 | Use the `useAuth0` hook in your components to access authentication state (`isLoading`, `isAuthenticated` and `user`) and authentication methods (`loginWithRedirect` and `logout`):
109 |
110 | ```jsx
111 | // src/App.js
112 | import React from 'react';
113 | import { useAuth0 } from '@auth0/auth0-react';
114 |
115 | function App() {
116 | const { isLoading, isAuthenticated, error, user, loginWithRedirect, logout } =
117 | useAuth0();
118 |
119 | if (isLoading) {
120 | return
Loading...
;
121 | }
122 | if (error) {
123 | return Oops... {error.message}
;
124 | }
125 |
126 | if (isAuthenticated) {
127 | return (
128 |
129 | Hello {user.name}{' '}
130 | logout({ logoutParams: { returnTo: window.location.origin } })}>
131 | Log out
132 |
133 |
134 | );
135 | } else {
136 | return loginWithRedirect()}>Log in ;
137 | }
138 | }
139 |
140 | export default App;
141 | ```
142 |
143 | For more code samples on how to integrate **auth0-react** SDK in your **React** application, have a look at our [examples](https://github.com/auth0/auth0-react/blob/main/EXAMPLES.md).
144 |
145 | ## API reference
146 |
147 | Explore public API's available in auth0-react.
148 |
149 | - [Auth0Provider](https://auth0.github.io/auth0-react/functions/Auth0Provider.html)
150 | - [Auth0ProviderOptions](https://auth0.github.io/auth0-react/interfaces/Auth0ProviderOptions.html)
151 | - [useAuth0](https://auth0.github.io/auth0-react/functions/useAuth0.html)
152 | - [withAuth0](https://auth0.github.io/auth0-react/functions/withAuth0.html)
153 | - [withAuthenticationRequired](https://auth0.github.io/auth0-react/functions/withAuthenticationRequired.html)
154 |
155 | ## Feedback
156 |
157 | ### Contributing
158 |
159 | We appreciate feedback and contribution to this repo! Before you get started, please see the following:
160 |
161 | - [Auth0's general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
162 | - [Auth0's code of conduct guidelines](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
163 | - [This repo's contribution guide](https://github.com/auth0/auth0-react/blob/main/CONTRIBUTING.md)
164 |
165 | ### Raise an issue
166 |
167 | To provide feedback or report a bug, please [raise an issue on our issue tracker](https://github.com/auth0/auth0-react/issues).
168 |
169 | ### Vulnerability Reporting
170 |
171 | Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/responsible-disclosure-policy) details the procedure for disclosing security issues.
172 |
173 | ---
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 | Auth0 is an easy to implement, adaptable authentication and authorization platform. To learn more checkout Why Auth0?
183 |
184 | This project is licensed under the MIT license. See the LICENSE file for more info.
185 |
--------------------------------------------------------------------------------
/TROUBLESHOOTING.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | When your application is not behaving as expected:
4 |
5 | - Check for any messages in the console
6 | - Check the Network Activity especially any requests to your authorization server
7 | - Check the log data in your [Auth0 Dashboard](https://manage.auth0.com#/logs)
8 | - Check the `@auth0/auth0-spa-js` [FAQs](https://github.com/auth0/auth0-spa-js/blob/main/FAQ.md)
9 |
10 | If you believe there is a bug in the SDK, [raise an issue](https://github.com/auth0/auth0-spa-js/issues/new/choose). Be sure to include all the information required to reproduce the issue.
11 |
--------------------------------------------------------------------------------
/__mocks__/@auth0/auth0-spa-js.tsx:
--------------------------------------------------------------------------------
1 | const handleRedirectCallback = jest.fn(() => ({ appState: {} }));
2 | const buildLogoutUrl = jest.fn();
3 | const buildAuthorizeUrl = jest.fn();
4 | const checkSession = jest.fn();
5 | const getTokenSilently = jest.fn();
6 | const getTokenWithPopup = jest.fn();
7 | const getUser = jest.fn();
8 | const getIdTokenClaims = jest.fn();
9 | const isAuthenticated = jest.fn(() => false);
10 | const loginWithPopup = jest.fn();
11 | const loginWithRedirect = jest.fn();
12 | const logout = jest.fn();
13 |
14 | export const Auth0Client = jest.fn(() => {
15 | return {
16 | buildAuthorizeUrl,
17 | buildLogoutUrl,
18 | checkSession,
19 | handleRedirectCallback,
20 | getTokenSilently,
21 | getTokenWithPopup,
22 | getUser,
23 | getIdTokenClaims,
24 | isAuthenticated,
25 | loginWithPopup,
26 | loginWithRedirect,
27 | logout,
28 | };
29 | });
30 |
--------------------------------------------------------------------------------
/__tests__/auth-reducer.test.tsx:
--------------------------------------------------------------------------------
1 | import { reducer } from '../src/reducer';
2 | import { initialAuthState } from '../src/auth-state';
3 |
4 | describe('reducer', () => {
5 | it('should initialise when authenticated', async () => {
6 | const payload = {
7 | isAuthenticated: true,
8 | user: { name: 'Bob' },
9 | };
10 | expect(
11 | reducer(initialAuthState, { type: 'INITIALISED', ...payload })
12 | ).toEqual({
13 | ...initialAuthState,
14 | isLoading: false,
15 | ...payload,
16 | });
17 | });
18 |
19 | it('should initialise when not authenticated', async () => {
20 | const payload = {
21 | isAuthenticated: false,
22 | };
23 | expect(
24 | reducer(initialAuthState, { type: 'INITIALISED', ...payload })
25 | ).toEqual({
26 | ...initialAuthState,
27 | isLoading: false,
28 | ...payload,
29 | });
30 | });
31 |
32 | it('should handle error state', async () => {
33 | const payload = {
34 | error: new Error('__test_error__'),
35 | };
36 | expect(reducer(initialAuthState, { type: 'ERROR', ...payload })).toEqual({
37 | ...initialAuthState,
38 | isLoading: false,
39 | ...payload,
40 | });
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/__tests__/errors.test.tsx:
--------------------------------------------------------------------------------
1 | import { OAuthError } from '../src';
2 |
3 | describe('OAuthError', () => {
4 | it('should produce an OAuth JS error with error_description properties', async () => {
5 | const error = new OAuthError(
6 | '__test_error__',
7 | '__test_error_description__'
8 | );
9 | expect(error.error).toBe('__test_error__');
10 | expect(error.error_description).toBe('__test_error_description__');
11 | expect(error.message).toBe('__test_error_description__');
12 | });
13 |
14 | it('should produce an OAuth JS error with error properties', async () => {
15 | const error = new OAuthError('__test_error__');
16 | expect(error.error).toBe('__test_error__');
17 | expect(error.message).toBe('__test_error__');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/__tests__/helpers.tsx:
--------------------------------------------------------------------------------
1 | import React, { PropsWithChildren } from 'react';
2 | import Auth0Provider, { Auth0ProviderOptions } from '../src/auth0-provider';
3 |
4 | export const createWrapper = ({
5 | clientId = '__test_client_id__',
6 | domain = '__test_domain__',
7 | ...opts
8 | }: Partial = {}) => {
9 | return function Wrapper({
10 | children,
11 | }: PropsWithChildren>): React.JSX.Element {
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | };
18 | };
19 |
20 | export interface Defer {
21 | resolve: (value: TData | PromiseLike) => void;
22 | reject: (reason?: unknown) => void;
23 | promise: Promise;
24 | }
25 |
26 | export function defer() {
27 | const deferred: Defer = {} as unknown as Defer;
28 |
29 | const promise = new Promise(function (resolve, reject) {
30 | deferred.resolve = resolve;
31 | deferred.reject = reject;
32 | });
33 |
34 | deferred.promise = promise;
35 | return deferred;
36 | }
37 |
--------------------------------------------------------------------------------
/__tests__/ssr.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @jest-environment node
3 | */
4 | import React from 'react';
5 | import ReactDOMServer from 'react-dom/server';
6 | import { Auth0Provider, Auth0Context } from '../src';
7 |
8 | jest.unmock('@auth0/auth0-spa-js');
9 |
10 | describe('In a Node SSR environment', () => {
11 | it('auth state is initialised', async () => {
12 | let isLoading, isAuthenticated, user, loginWithRedirect;
13 | ReactDOMServer.renderToString(
14 |
15 |
16 | {(value): React.JSX.Element => {
17 | ({ isLoading, isAuthenticated, user, loginWithRedirect } = value);
18 | return App
;
19 | }}
20 |
21 |
22 | );
23 | expect(isLoading).toBeTruthy();
24 | expect(isAuthenticated).toBeFalsy();
25 | expect(user).toBeUndefined();
26 | await expect(loginWithRedirect).rejects.toThrowError(
27 | 'window is not defined'
28 | );
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/__tests__/use-auth.test.tsx:
--------------------------------------------------------------------------------
1 | import { act, renderHook, waitFor } from '@testing-library/react';
2 | import React from 'react';
3 | import { Auth0ContextInterface, initialContext } from '../src/auth0-context';
4 | import useAuth0 from '../src/use-auth0';
5 | import { createWrapper } from './helpers';
6 |
7 | describe('useAuth0', () => {
8 | it('should provide the auth context', async () => {
9 | const wrapper = createWrapper();
10 | const {
11 | result: { current }
12 | } = renderHook(() => useAuth0(), { wrapper });
13 | await waitFor(() => {
14 | expect(current).toBeDefined();
15 | });
16 | });
17 |
18 | it('should throw with no provider', () => {
19 | const {
20 | result: { current },
21 | } = renderHook(() => useAuth0());
22 | expect(current.loginWithRedirect).toThrowError(
23 | 'You forgot to wrap your component in .'
24 | );
25 | });
26 |
27 | it('should throw when context is not associated with provider', async () => {
28 | const context = React.createContext(initialContext);
29 | const wrapper = createWrapper({ context });
30 | const {
31 | result: { current },
32 | } = renderHook(() => useAuth0(), { wrapper });
33 | await act(async () => {
34 | expect(current.loginWithRedirect).toThrowError(
35 | 'You forgot to wrap your component in .'
36 | );
37 | });
38 | });
39 |
40 | it('should accept custom auth context', async () => {
41 | const context = React.createContext(initialContext);
42 | const wrapper = createWrapper({ context });
43 | const {
44 | result: { current },
45 | } = renderHook(() => useAuth0(context), { wrapper });
46 | await waitFor(() => {
47 | expect(current).toBeDefined();
48 | expect(current.loginWithRedirect).not.toThrowError();
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/__tests__/utils.test.tsx:
--------------------------------------------------------------------------------
1 | import { hasAuthParams, loginError, tokenError } from '../src/utils';
2 | import { OAuthError } from '../src/errors';
3 |
4 | describe('utils hasAuthParams', () => {
5 | it('should not recognise only the code param', async () => {
6 | ['?code=1', '?foo=1&code=2', '?code=1&foo=2'].forEach((search) =>
7 | expect(hasAuthParams(search)).toBeFalsy()
8 | );
9 | });
10 |
11 | it('should recognise the code and state param', async () => {
12 | [
13 | '?code=1&state=2',
14 | '?foo=1&state=2&code=3',
15 | '?code=1&foo=2&state=3',
16 | '?state=1&code=2&foo=3',
17 | ].forEach((search) => expect(hasAuthParams(search)).toBeTruthy());
18 | });
19 |
20 | it('should recognise the error and state param', async () => {
21 | [
22 | '?error=1&state=2',
23 | '?foo=1&state=2&error=3',
24 | '?error=1&foo=2&state=3',
25 | '?state=1&error=2&foo=3',
26 | ].forEach((search) => expect(hasAuthParams(search)).toBeTruthy());
27 | });
28 |
29 | it('should ignore the error param without state param', async () => {
30 | ['?error=1', '?foo=1&error=2', '?error=1&foo=2'].forEach((search) =>
31 | expect(hasAuthParams(search)).toBeFalsy()
32 | );
33 | });
34 |
35 | it('should ignore invalid params', async () => {
36 | ['', '?', '?foo=1', '?code=&foo=2', '?error='].forEach((search) =>
37 | expect(hasAuthParams(search)).toBeFalsy()
38 | );
39 | });
40 | });
41 |
42 | describe('utils error', () => {
43 | it('should return the original error', async () => {
44 | const error = new Error('__test_error__');
45 | expect(loginError(error)).toBe(error);
46 | });
47 |
48 | it('should convert OAuth error data to an OAuth JS error', async () => {
49 | const error = {
50 | error: '__test_error__',
51 | error_description: '__test_error_description__',
52 | };
53 | expect(() => {
54 | throw tokenError(error);
55 | }).toThrow(OAuthError);
56 | });
57 |
58 | it('should convert a ProgressEvent error to a JS error', async () => {
59 | const error = new ProgressEvent('error');
60 | expect(() => {
61 | throw loginError(error);
62 | }).toThrowError('Login failed');
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/__tests__/with-auth0.test.tsx:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import { render, screen } from '@testing-library/react';
3 | import React, { Component } from 'react';
4 | import { Auth0ContextInterface, initialContext } from '../src/auth0-context';
5 | import withAuth0, { WithAuth0Props } from '../src/with-auth0';
6 |
7 | describe('withAuth0', () => {
8 | it('should wrap a class component', () => {
9 | class MyComponent extends Component {
10 | render() {
11 | return <>hasAuth: {`${!!this.props.auth0}`}>;
12 | }
13 | }
14 | const WrappedComponent = withAuth0(MyComponent);
15 | render( );
16 | expect(screen.getByText('hasAuth: true')).toBeInTheDocument();
17 | });
18 |
19 | it('should wrap a class component and provide context', () => {
20 | const context = React.createContext(initialContext);
21 | class MyComponent extends Component {
22 | render() {
23 | return <>hasAuth: {`${!!this.props.auth0}`}>;
24 | }
25 | }
26 | const WrappedComponent = withAuth0(MyComponent, context);
27 | render( );
28 | expect(screen.getByText('hasAuth: true')).toBeInTheDocument();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/browserstack.json:
--------------------------------------------------------------------------------
1 | {
2 | "browsers": [
3 | {
4 | "browser": "chrome",
5 | "os": "Windows 10",
6 | "versions": ["latest"]
7 | },
8 | {
9 | "browser": "firefox",
10 | "os": "Windows 10",
11 | "versions": ["latest"]
12 | },
13 | {
14 | "browser": "edge",
15 | "os": "Windows 10",
16 | "versions": ["latest"]
17 | }
18 | ],
19 | "run_settings": {
20 | "cypress_config_file": "./cypress-bs.config.js",
21 | "cypress-version": "13.1",
22 | "project_name": "Auth0 React SDK",
23 | "exclude": [],
24 | "parallels": "5",
25 | "npm_dependencies": {
26 | "typescript": "^4.6.3"
27 | },
28 | "package_config_options": {},
29 | "headless": true
30 | },
31 | "connection_settings": {
32 | "local": true,
33 | "local_mode": "always-on"
34 | },
35 | "disable_usage_reporting": false
36 | }
37 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment: false
2 |
--------------------------------------------------------------------------------
/cypress-bs.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress');
2 |
3 | module.exports = defineConfig({
4 | defaultCommandTimeout: 7500,
5 | chromeWebSecurity: false,
6 | viewportWidth: 1000,
7 | viewportHeight: 1000,
8 | fixturesFolder: false,
9 | reporter: 'junit',
10 | reporterOptions: {
11 | mochaFile: 'test-results/cypress/junit-[hash].xml',
12 | },
13 | e2e: {
14 | setupNodeEvents(on, config) {},
15 | baseUrl: 'http://127.0.0.1:3000',
16 | supportFile: false,
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress');
2 |
3 | module.exports = defineConfig({
4 | defaultCommandTimeout: 7500,
5 | chromeWebSecurity: false,
6 | viewportWidth: 1000,
7 | viewportHeight: 1000,
8 | fixturesFolder: false,
9 | reporter: 'junit',
10 | reporterOptions: {
11 | mochaFile: 'test-results/cypress/junit-[hash].xml',
12 | },
13 | e2e: {
14 | setupNodeEvents(on, config) {},
15 | baseUrl: 'http://localhost:3000',
16 | supportFile: false,
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/cypress/e2e/smoke-bs.cy.ts:
--------------------------------------------------------------------------------
1 | const EMAIL = 'test';
2 | const PASSWORD = 'test';
3 |
4 | if (!EMAIL || !PASSWORD) {
5 | throw new Error(
6 | 'You must provide CYPRESS_USER_EMAIL and CYPRESS_USER_PASSWORD environment variables'
7 | );
8 | }
9 |
10 | const loginToNodeOidc = (): void => {
11 | cy.get('input[name=login]').clear().type(EMAIL);
12 | cy.get('input[name=password]').clear().type(PASSWORD);
13 | cy.get('.login-submit').click();
14 | cy.get('.login-submit').click();
15 | };
16 |
17 | const login = (): void => {
18 | return loginToNodeOidc();
19 | };
20 |
21 | const fixCookies = () => {
22 | // Temporary fix for https://github.com/cypress-io/cypress/issues/6375
23 | if (Cypress.isBrowser('firefox')) {
24 | cy.getCookies({ log: false }).then((cookies) =>
25 | cookies.forEach((cookie) => cy.clearCookie(cookie.name, { log: false }))
26 | );
27 | cy.log('clearCookies');
28 | } else {
29 | cy.clearCookies();
30 | }
31 | };
32 |
33 | describe('Smoke tests', () => {
34 | afterEach(fixCookies);
35 |
36 | it('do basic login and show user', () => {
37 | cy.visit('/');
38 |
39 | cy.get('[data-cy=use-node-oidc-provider]').click();
40 | cy.get('#login').click();
41 |
42 | login();
43 |
44 | cy.get('#hello').contains(`Hello, ${EMAIL}!`);
45 | cy.get('#logout').click();
46 | cy.get('button[name=logout]').click();
47 | cy.get('#login').should('exist');
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/cypress/e2e/smoke.cy.ts:
--------------------------------------------------------------------------------
1 | const EMAIL = Cypress.env('USER_EMAIL');
2 | const PASSWORD = Cypress.env('USER_PASSWORD');
3 |
4 | if (!EMAIL || !PASSWORD) {
5 | throw new Error(
6 | 'You must provide CYPRESS_USER_EMAIL and CYPRESS_USER_PASSWORD environment variables'
7 | );
8 | }
9 |
10 | const loginToAuth0 = (): void => {
11 | cy.get('.auth0-lock-input-username .auth0-lock-input').clear().type(EMAIL);
12 | cy.get('.auth0-lock-input-password .auth0-lock-input').clear().type(PASSWORD);
13 | cy.get('.auth0-lock-submit').click();
14 | };
15 |
16 | describe('Smoke tests', () => {
17 | it('do basic login and show user', () => {
18 | cy.visit('/');
19 | cy.get('#login').should('be.visible');
20 | cy.get('#login').click();
21 |
22 | loginToAuth0();
23 |
24 | cy.get('#hello').contains(`Hello, ${EMAIL}!`);
25 | cy.get('#logout').click();
26 | cy.get('#login').should('exist');
27 | });
28 |
29 | it('should protect a route and return to path after login', () => {
30 | cy.visit('/users');
31 |
32 | loginToAuth0();
33 |
34 | // Make sure the table has rendered with data as that is when the page has loaded completely
35 | // and there shouldn't be any issues with the logout button being recreated
36 | cy.get('table tbody tr').should('have.length', 2);
37 | cy.url().should('include', '/users');
38 | cy.get('#logout').click();
39 | });
40 |
41 | it('should access an api', () => {
42 | cy.visit('/users');
43 |
44 | loginToAuth0();
45 |
46 | // Make sure the table has rendered with data as that is when the page has loaded completely
47 | // and there shouldn't be any issues with the logout button being recreated
48 | cy.get('table tbody tr').should('have.length', 2);
49 | cy.get('table').contains('bob@example.com');
50 | cy.get('#logout').click();
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "baseUrl": "../node_modules",
5 | "target": "es5",
6 | "lib": ["es5", "dom"],
7 | "types": ["cypress"]
8 | },
9 | "include": ["**/*.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
--------------------------------------------------------------------------------
/docs/assets/highlight.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --light-hl-0: #795E26;
3 | --dark-hl-0: #DCDCAA;
4 | --light-hl-1: #000000;
5 | --dark-hl-1: #D4D4D4;
6 | --light-hl-2: #A31515;
7 | --dark-hl-2: #CE9178;
8 | --light-hl-3: #008000;
9 | --dark-hl-3: #6A9955;
10 | --light-hl-4: #AF00DB;
11 | --dark-hl-4: #C586C0;
12 | --light-hl-5: #001080;
13 | --dark-hl-5: #9CDCFE;
14 | --light-hl-6: #0000FF;
15 | --dark-hl-6: #569CD6;
16 | --light-hl-7: #0070C1;
17 | --dark-hl-7: #4FC1FF;
18 | --light-hl-8: #800000;
19 | --dark-hl-8: #808080;
20 | --light-hl-9: #267F99;
21 | --dark-hl-9: #4EC9B0;
22 | --light-hl-10: #E50000;
23 | --dark-hl-10: #9CDCFE;
24 | --light-hl-11: #000000FF;
25 | --dark-hl-11: #D4D4D4;
26 | --light-hl-12: #800000;
27 | --dark-hl-12: #569CD6;
28 | --light-hl-13: #098658;
29 | --dark-hl-13: #B5CEA8;
30 | --light-code-background: #FFFFFF;
31 | --dark-code-background: #1E1E1E;
32 | }
33 |
34 | @media (prefers-color-scheme: light) { :root {
35 | --hl-0: var(--light-hl-0);
36 | --hl-1: var(--light-hl-1);
37 | --hl-2: var(--light-hl-2);
38 | --hl-3: var(--light-hl-3);
39 | --hl-4: var(--light-hl-4);
40 | --hl-5: var(--light-hl-5);
41 | --hl-6: var(--light-hl-6);
42 | --hl-7: var(--light-hl-7);
43 | --hl-8: var(--light-hl-8);
44 | --hl-9: var(--light-hl-9);
45 | --hl-10: var(--light-hl-10);
46 | --hl-11: var(--light-hl-11);
47 | --hl-12: var(--light-hl-12);
48 | --hl-13: var(--light-hl-13);
49 | --code-background: var(--light-code-background);
50 | } }
51 |
52 | @media (prefers-color-scheme: dark) { :root {
53 | --hl-0: var(--dark-hl-0);
54 | --hl-1: var(--dark-hl-1);
55 | --hl-2: var(--dark-hl-2);
56 | --hl-3: var(--dark-hl-3);
57 | --hl-4: var(--dark-hl-4);
58 | --hl-5: var(--dark-hl-5);
59 | --hl-6: var(--dark-hl-6);
60 | --hl-7: var(--dark-hl-7);
61 | --hl-8: var(--dark-hl-8);
62 | --hl-9: var(--dark-hl-9);
63 | --hl-10: var(--dark-hl-10);
64 | --hl-11: var(--dark-hl-11);
65 | --hl-12: var(--dark-hl-12);
66 | --hl-13: var(--dark-hl-13);
67 | --code-background: var(--dark-code-background);
68 | } }
69 |
70 | :root[data-theme='light'] {
71 | --hl-0: var(--light-hl-0);
72 | --hl-1: var(--light-hl-1);
73 | --hl-2: var(--light-hl-2);
74 | --hl-3: var(--light-hl-3);
75 | --hl-4: var(--light-hl-4);
76 | --hl-5: var(--light-hl-5);
77 | --hl-6: var(--light-hl-6);
78 | --hl-7: var(--light-hl-7);
79 | --hl-8: var(--light-hl-8);
80 | --hl-9: var(--light-hl-9);
81 | --hl-10: var(--light-hl-10);
82 | --hl-11: var(--light-hl-11);
83 | --hl-12: var(--light-hl-12);
84 | --hl-13: var(--light-hl-13);
85 | --code-background: var(--light-code-background);
86 | }
87 |
88 | :root[data-theme='dark'] {
89 | --hl-0: var(--dark-hl-0);
90 | --hl-1: var(--dark-hl-1);
91 | --hl-2: var(--dark-hl-2);
92 | --hl-3: var(--dark-hl-3);
93 | --hl-4: var(--dark-hl-4);
94 | --hl-5: var(--dark-hl-5);
95 | --hl-6: var(--dark-hl-6);
96 | --hl-7: var(--dark-hl-7);
97 | --hl-8: var(--dark-hl-8);
98 | --hl-9: var(--dark-hl-9);
99 | --hl-10: var(--dark-hl-10);
100 | --hl-11: var(--dark-hl-11);
101 | --hl-12: var(--dark-hl-12);
102 | --hl-13: var(--dark-hl-13);
103 | --code-background: var(--dark-code-background);
104 | }
105 |
106 | .hl-0 { color: var(--hl-0); }
107 | .hl-1 { color: var(--hl-1); }
108 | .hl-2 { color: var(--hl-2); }
109 | .hl-3 { color: var(--hl-3); }
110 | .hl-4 { color: var(--hl-4); }
111 | .hl-5 { color: var(--hl-5); }
112 | .hl-6 { color: var(--hl-6); }
113 | .hl-7 { color: var(--hl-7); }
114 | .hl-8 { color: var(--hl-8); }
115 | .hl-9 { color: var(--hl-9); }
116 | .hl-10 { color: var(--hl-10); }
117 | .hl-11 { color: var(--hl-11); }
118 | .hl-12 { color: var(--hl-12); }
119 | .hl-13 { color: var(--hl-13); }
120 | pre, code { background: var(--code-background); }
121 |
--------------------------------------------------------------------------------
/docs/assets/navigation.js:
--------------------------------------------------------------------------------
1 | window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE6WWXW+bMBSG/4uv062N1q7L3RRNU6RUjZJWu5h24cIBrDo2sw9b06r/fTKMBPAHyLkM5/XzOMY+5ucbQXhBsiBfKyxAIEsoMim+KSUVmZGSYkEWJOFUa9AfHaEPBe45mZFnJlKyuJrfzpo0WRDU6QXTF/CCoATl5H12tH0HAYolHk23GsdfiTvYS3VY0qQAW9ArxxnWMqF8h1LRHDwWKxJnusvoFn5XTEHqWa9hItLDtGYi30KmQBcP8hl828CXjPPem13lMZ1qFrtD2MiyKpdUJMC5d5Ecobj51qAHtgdZYcjVjcSZwpLz+Y8aHFzzNI5nXtblUgrzcyUQVEaTzrlg7aOmkVjJvnR+fTNEb5T8w1JQ96VpP9pPHgTHwFKx17qjbaiiez93kLOwE1sf1idmxzgI5IfQn/FkzxP/YFjUG3SKeRiOU68GHbIjWjk642RsWs/RzW1qceC1zGWFofXpJUL7qwk+Kj5OO4XiZt10OCkylodkduwM3VrmTIzauqk42RZSpiDBUZ8rGHo/ZoO3PaN0I/uRKbDTN1J7L4emPDoq2L/KcocUO4cLD6XpV/+fD8Zefvl8dT3vjK+Pn/lSMaYhpFd0kia9vBpDn7g1yWMhnt29SU74rBJJvXS9m6Zvufnku1+8nDYQAFUa6qiL0dYCw/+2e801/licALA3U4hopy3Fr3+FU0zaLAwAAA=="
--------------------------------------------------------------------------------
/docs/assets/search.js:
--------------------------------------------------------------------------------
1 | window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE81ayW7bSBD9l9aVltWLqOWWCeYQIECCJJg5EIbBUG2bME1ySCoLDP/7oLlWUV1cpATOSbBZVf36vdfNYpPPLEu+52zvPbPHMD6wvascFvtPmu3Zm2PxsPqYJd/Cg86Yw45ZxPbs7hgHRZjE+TW6vnwoniLmsCDy81znbM/Yi9MUFWvXXvVDWpZqi4dxobM7P9C96nXc4CB8JTrswUMYHTIdz6q8AFlgCIelfqbjggJPQEjiT/oQZjoo3vpR9NUPHueBseafBctdr2UnwO1t8TPVl2JZVmWu1CxQS8ukCPryxzC9jECiwuXKBklc6B/FTG+1SWcBEKvdhq9Ft4zS9HPhF52ORo38uvn34EoZ9IOlzqINIaA3WAi+Ml0cs/hLMmGMJYgdHqw2IJxWN+Ix1yWfll2ruTTMUFfqe1g8ULXaa5N3v3+bjI9Zklr3PRwxfcfzEcbhgosm2E5xD+QgLTouwsA3dHzS/x3DTB8GeDoNnk3caYmBe8ho0nR6Tzw8e5jFqLXH5zh6qwnj+wsQ9utcDnPqreccgM0tSJyPdIlnTNL7l75LMo1LXTQNouAfSLgN6XLkhjCFdysDhABRch/GF6/zRa/ML16EA13BRHxjLcIsH/T6+Lc9dL02vr48r4uvk941EyUbon7g9G33XhdvgkDn+ZfkUcefw0jHRfRz3kALsshAJ3YytzPb6TmIxpaVvSAxu0mEGkd9TNJjehF+WOUPobSFNPE5ZYTVboY0re8OZejbyA+f6CdaEnw//TWJRFgaBt35DOI5DW3uhuDmTjwTry3/1cg7AdOwx+kuxV7udFpj/J2zkE+SX585tGw5/ZQyQtvgco2S++R4htGqpNfkKDl2lpq7p9UTIDh58ONDpCcfudgRkkVejTM7oobD7UwKifkRlOosS7KZeJucDlWRH67C/CqMH3QWFuUD9mQCEZwwB90jeFCfBuw0+7dAfJ/4B+IpdhBcl/cbYB1zPVfHOuXXgIGt9/tyFQ88DaGAyR194+n3I89atrjhfl5s20E+mIn+jVZFnXPdXRqstuYCPnHlRXYMivFyCxxrX/IA3aQFTY11uoTPGOX2oPMgC1N01DA4Yi9jfPQbh4XxQf9g+2f2TWe5ydszsZTLHXPYXaijg3k1VMFyWJA8PZkiN/W1f7Rh00RUIdcr5ngrR66XK9e9uXG8JqO8UP6jDOPM8bgtjKMwwRxP2MIECpPM8aQtTKIwxRxPOVwt3d0GhSkUtmaOt7ZVW6Mwlzme64jdcrfG1VwUtmGOt7FV26CwLYVti8J2zPG2tkF3mF7D9s7Kb0+HUgirYBxLYZpXj9s1w2pwwzq3ysaxIKZ78bhVOY414YZ7rqyRWBbuUhRxLAw3AnCr0Bxrw0lxOFaHGxW4ay2JBRIrqqTAAolSIKuBRG+tCMqQAusjSn221pJYH1HqY/WRwPoII4Kw+khgfYRLzhzrI4wIwmo4gfURpD4C6yOMCMK+oWB9JKmPxPpII4Kwbz5YHynIkr3dzIggrF6XWB9J7mgSyyNLeaxel1geScojsTyylMfqdYnlkaQ8EssjS3k2NgtLLI8yIgirhRXWR5X6WC2ssD7KqCCtFlZYIFXeb6zOVL07jpFBWg2nsELKyCCtPlJYIWV0kFZ7KCyR2pB8KiyRMkJIqz9UpVHZJ3zTWaEP76p+wfPah7Fndls3EW0D88wU2z+/OGxb/fD6V6zqX7f+rf8v6/9LUf/W6bKOk2XcS9eBmL8MTj9N8+oleIdi06HYUGnV288up+spnxmXQ1ntET1I3nbJYkpy20yjKjtQRQ1VSdsvcrrsVZe8mpKbNP09AAAoIEp0n8d0aYA5QaXB1rvLlNAvLp3b59vt0hofUSPXjThAC9whd1W2omxiaaoB+jVAT/nzXhd+eYhcmGPQvH3xAAAB5cR6UhnzMj2tTutAHSCfoCZ0r4vwUNYI6lNmUAAKuSMKVKcfWf0EGLSnO6AMUEdSOMLcxycJIB8sJkVZOcyj5mEfZIIFpCgHl6eDNvMDX4jBZDv7wMqS2gLa9Kw9pwYVJKgwAKA8gARpwIWSsk+VZpm1BKZRFOyk1Kq/kCTAq6iBk/hr+ZLX773mBrwDu3DKdUk85DhI3GiBnmc4oI9Tbm1yKe9IsHIUtXNX334UCUwEVq+3Ik5tg+ZTtiESwDwoMY65PrnvgSXDqcVWnWOB6YIdS1Ebr7H56V0WZHLK421mWn0gBdLhHWps4M5uWftZEigFTMMpyehStg0E7ADcKsGNw9Iw1VEYa7b3bl5e/gfBQ3fmeysAAA==";
--------------------------------------------------------------------------------
/docs/classes/InMemoryCache.html:
--------------------------------------------------------------------------------
1 | InMemoryCache | @auth0/auth0-react Defined in node_modules/@auth0/auth0-spa-js/dist/typings/cache/cache-memory.d.ts:2 Properties enclosed Cache Defined in node_modules/@auth0/auth0-spa-js/dist/typings/cache/cache-memory.d.ts:3
--------------------------------------------------------------------------------
/docs/functions/Auth0Provider.html:
--------------------------------------------------------------------------------
1 | Auth0Provider | @auth0/auth0-react Auth0 Provider ( opts ) : Element Returns Element
--------------------------------------------------------------------------------
/docs/functions/withAuthenticationRequired.html:
--------------------------------------------------------------------------------
1 | withAuthenticationRequired | @auth0/auth0-react Function withAuthenticationRequired with Authentication Required < P > ( Component , options ? ) : FC < P > Returns FC < P >
--------------------------------------------------------------------------------
/docs/hierarchy.html:
--------------------------------------------------------------------------------
1 | @auth0/auth0-react
@auth0/auth0-react Class Hierarchy
--------------------------------------------------------------------------------
/docs/interfaces/PopupConfigOptions.html:
--------------------------------------------------------------------------------
1 | PopupConfigOptions | @auth0/auth0-react Interface PopupConfigOptions Defined in node_modules/@auth0/auth0-spa-js/dist/typings/global.d.ts:311 Properties Optional
popup popup ?: any
Defined in node_modules/@auth0/auth0-spa-js/dist/typings/global.d.ts:322 Optional
timeout In Seconds timeout In Seconds ?: number
Defined in node_modules/@auth0/auth0-spa-js/dist/typings/global.d.ts:316
--------------------------------------------------------------------------------
/docs/interfaces/PopupLoginOptions.html:
--------------------------------------------------------------------------------
1 | PopupLoginOptions | @auth0/auth0-react Interface PopupLoginOptions Defined in node_modules/@auth0/auth0-spa-js/dist/typings/global.d.ts:309 Properties Optional
authorization Params
--------------------------------------------------------------------------------
/docs/interfaces/WithAuth0Props.html:
--------------------------------------------------------------------------------
1 | WithAuth0Props | @auth0/auth0-react
--------------------------------------------------------------------------------
/docs/types/AppState.html:
--------------------------------------------------------------------------------
1 | AppState | @auth0/auth0-react App State : { returnTo ?: string ; [key : string ]: any ; }
Type declaration [ key : string ]: any Optional
return To ?: string
--------------------------------------------------------------------------------
/docs/types/CacheLocation.html:
--------------------------------------------------------------------------------
1 | CacheLocation | @auth0/auth0-react Cache Location : "memory" | "localstorage"
Defined in node_modules/@auth0/auth0-spa-js/dist/typings/global.d.ts:247
--------------------------------------------------------------------------------
/docs/types/Cacheable.html:
--------------------------------------------------------------------------------
1 | Cacheable | @auth0/auth0-react Cacheable : WrappedCacheEntry | KeyManifestEntry
Defined in node_modules/@auth0/auth0-spa-js/dist/typings/cache/shared.d.ts:60
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # @auth0/auth0-react Examples
2 |
3 | ## Configure an Auth0 application, api and user:
4 |
5 | - Follow the steps to configure an Auth0 Single-Page Application (SPA) in https://auth0.com/docs/quickstart/spa/react/01-login#configure-auth0
6 | - Follow the steps to create an API in https://auth0.com/docs/quickstart/spa/react/02-calling-an-api#create-an-api
7 | - Add a permission to your API of `read:users` following the steps in https://auth0.com/docs/dashboard/guides/apis/add-permissions-apis
8 |
9 | ### Follow the steps to run each of the example applications:
10 |
11 | - [Create React App](./cra-react-router/README.md)
12 | - [Gatsby](./gatsby-app/README.md)
13 | - [NextJS](./nextjs-app/README.md)
14 | - [Users API](./users-api/README.md)
15 |
--------------------------------------------------------------------------------
/examples/cra-react-router/.env.sample:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
2 | REACT_APP_DOMAIN=your-tenant.auth0.com
3 | REACT_APP_CLIENT_ID=yourclientid
4 | REACT_APP_AUDIENCE=https://api.example.com/users
5 | REACT_APP_API_PORT=3001
6 |
--------------------------------------------------------------------------------
/examples/cra-react-router/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | # Intentionally removing package lock because it has problems when using local file resolutions
26 | package-lock.json
27 |
--------------------------------------------------------------------------------
/examples/cra-react-router/README.md:
--------------------------------------------------------------------------------
1 | # React Router example
2 |
3 | This is an example of using `@auth0/auth0-react` with `react-router`.
4 |
5 | Follow the steps in [examples/README.md](../README.md) to setup an Auth0 application and API.
6 |
7 | Add the file `./examples/cra-react-router/.env` with the `domain` and `clientId` of the application and `audience` (your API identifier)
8 |
9 | ```dotenv
10 | REACT_APP_DOMAIN=your_domain
11 | REACT_APP_CLIENT_ID=your_client_id
12 | REACT_APP_AUDIENCE=your_audience
13 | SKIP_PREFLIGHT_CHECK=true # To workaround issues with nesting create-react-app in another package
14 | ```
15 |
16 | Run `npm start` to start the application at http://localhost:3000
17 |
18 | Start the API using the instructions in [examples/users-api/README.md](../users-api/README.md)
19 |
--------------------------------------------------------------------------------
/examples/cra-react-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cra-react-router",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@auth0/auth0-react": "2.2.4",
7 | "@types/node": "^17.0.29",
8 | "@types/react": "18.3.18",
9 | "@types/react-dom": "18.3.5",
10 | "react": "18.3.1",
11 | "react-dom": "18.3.1",
12 | "react-router-dom": "^6.3.0",
13 | "react-scripts": "^5.0.1",
14 | "typescript": "^4.6.3"
15 | },
16 | "devDependencies": {
17 | "ajv": "8.16.0"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build"
22 | },
23 | "eslintConfig": {
24 | "extends": "react-app"
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/cra-react-router/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0/auth0-react/1644bb53f7ef1bc5b62a904a0908587b3f12dd54/examples/cra-react-router/public/favicon.ico
--------------------------------------------------------------------------------
/examples/cra-react-router/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
18 | React App
19 |
20 |
21 | You need to enable JavaScript to run this app.
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/examples/cra-react-router/src/App.css:
--------------------------------------------------------------------------------
1 | .spinner-border {
2 | top: 50%;
3 | position: fixed;
4 | margin-top: -1rem;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/cra-react-router/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useAuth0, withAuthenticationRequired } from '@auth0/auth0-react';
3 | import { Route, Routes } from 'react-router-dom';
4 | import './App.css';
5 | import { Nav } from './Nav';
6 | import { Error } from './Error';
7 | import { Loading } from './Loading';
8 | import { Users } from './Users';
9 |
10 | const ProtectedUsers = withAuthenticationRequired(Users);
11 |
12 | function App() {
13 | const { isLoading, error } = useAuth0();
14 |
15 | if (isLoading) {
16 | return ;
17 | }
18 |
19 | return (
20 | <>
21 |
22 | {error && }
23 |
24 |
25 | } />
26 |
27 | >
28 | );
29 | }
30 |
31 | export default App;
32 |
--------------------------------------------------------------------------------
/examples/cra-react-router/src/Error.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function Error({ message }: { message: string }) {
4 | return (
5 |
6 | Oops... {message}
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/examples/cra-react-router/src/Loading.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function Loading() {
4 | return (
5 |
6 |
7 | Loading...
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/examples/cra-react-router/src/Nav.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useLocation, Link } from 'react-router-dom';
3 | import { useAuth0 } from '@auth0/auth0-react';
4 |
5 | export function Nav() {
6 | const { isAuthenticated, user, loginWithRedirect, logout } = useAuth0<{
7 | name: string;
8 | }>();
9 | const { pathname } = useLocation();
10 |
11 | return (
12 |
13 | @auth0/auth0-react
14 |
15 |
16 |
20 | Home
21 |
22 |
28 | Users
29 |
30 |
31 |
32 |
33 | {isAuthenticated ? (
34 |
35 | Hello, {user?.name}! {' '}
36 |
40 | logout({ logoutParams: { returnTo: window.location.origin } })
41 | }
42 | >
43 | logout
44 |
45 |
46 | ) : (
47 | loginWithRedirect()}
51 | >
52 | login
53 |
54 | )}
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/examples/cra-react-router/src/Users.tsx:
--------------------------------------------------------------------------------
1 | import { useApi } from './use-api';
2 | import React from 'react';
3 | import { Loading } from './Loading';
4 | import { Error } from './Error';
5 |
6 | const PORT = process.env.REACT_APP_API_PORT || 3001;
7 |
8 | export function Users() {
9 | const {
10 | loading,
11 | error,
12 | data: users = [],
13 | } = useApi(`http://localhost:${PORT}/users`, {
14 | audience: process.env.REACT_APP_AUDIENCE,
15 | scope: 'profile email read:users',
16 | });
17 |
18 | if (loading) {
19 | return ;
20 | }
21 |
22 | if (error) {
23 | return ;
24 | }
25 |
26 | return (
27 |
28 |
29 |
30 | Name
31 | Email
32 |
33 |
34 |
35 | {users!.map(
36 | ({ name, email }: { name: string; email: string }, i: number) => (
37 |
38 | {name}
39 | {email}
40 |
41 | )
42 | )}
43 |
44 |
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/examples/cra-react-router/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from "react-dom/client";
2 | import React, { PropsWithChildren } from 'react';
3 | import App from './App';
4 | import { Auth0Provider, AppState, Auth0ContextInterface, User } from '@auth0/auth0-react';
5 | import { BrowserRouter, useNavigate } from 'react-router-dom';
6 | import { Auth0ProviderOptions } from '../../../src/index.js';
7 |
8 | const Auth0ProviderWithRedirectCallback = ({
9 | children,
10 | context,
11 | ...props
12 | }: PropsWithChildren> & {
13 | context?: React.Context>
14 | }) => {
15 | const navigate = useNavigate();
16 |
17 | const onRedirectCallback = (appState?: AppState, user?: User) => {
18 | navigate((appState?.returnTo) || window.location.pathname);
19 | };
20 |
21 | return (
22 |
27 | {children}
28 |
29 | );
30 | };
31 | const root = createRoot(document.getElementById('root')!);
32 |
33 | root.render(
34 |
35 |
36 |
45 |
46 |
47 |
48 |
49 | );
50 |
--------------------------------------------------------------------------------
/examples/cra-react-router/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace NodeJS {
2 | interface ProcessEnv {
3 | REACT_APP_DOMAIN: string;
4 | REACT_APP_CLIENT_ID: string;
5 | REACT_APP_AUDIENCE: string;
6 | REACT_APP_API_PORT: number;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/cra-react-router/src/use-api.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { useAuth0 } from '@auth0/auth0-react';
3 |
4 | export const useApi = (
5 | url: string,
6 | options: any = {}
7 | ): { error?: Error | null; loading: boolean; data?: any } => {
8 | const { getAccessTokenSilently } = useAuth0();
9 | const [state, setState] = useState({
10 | error: null,
11 | loading: true,
12 | data: null,
13 | });
14 |
15 | useEffect(() => {
16 | (async () => {
17 | try {
18 | const { audience, scope, ...fetchOptions } = options;
19 | const accessToken = await getAccessTokenSilently({
20 | authorizationParams: { audience, scope },
21 | });
22 | const res = await fetch(url, {
23 | ...fetchOptions,
24 | headers: {
25 | ...fetchOptions.headers,
26 | // Add the Authorization header to the existing headers
27 | Authorization: `Bearer ${accessToken}`,
28 | },
29 | });
30 | setState({
31 | ...state,
32 | data: await res.json(),
33 | error: null,
34 | loading: false,
35 | });
36 | } catch (error: any) {
37 | setState({
38 | ...state,
39 | error,
40 | loading: false,
41 | });
42 | }
43 | })();
44 | }, []); // eslint-disable-line react-hooks/exhaustive-deps
45 |
46 | return state;
47 | };
48 |
--------------------------------------------------------------------------------
/examples/cra-react-router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "module": "NodeNext",
12 | "moduleResolution": "NodeNext",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "noEmit": true,
16 | "jsx": "react",
17 | "noFallthroughCasesInSwitch": true,
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/gatsby-app/.env.development.sample:
--------------------------------------------------------------------------------
1 | GATSBY_DOMAIN=your-tenant.auth0.com
2 | GATSBY_CLIENT_ID=yourclientid
3 | GATSBY_AUDIENCE=https://api.example.com/users
4 | GATSBY_API_PORT=3001
5 |
--------------------------------------------------------------------------------
/examples/gatsby-app/.eslintrc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/auth0/auth0-react/1644bb53f7ef1bc5b62a904a0908587b3f12dd54/examples/gatsby-app/.eslintrc
--------------------------------------------------------------------------------
/examples/gatsby-app/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # dotenv environment variable files
55 | .env.development
56 |
57 | # gatsby files
58 | .cache/
59 | public
60 |
61 | # Mac files
62 | .DS_Store
63 |
64 | # Yarn
65 | yarn-error.log
66 | .pnp/
67 | .pnp.js
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # Intentionally removing package lock because it has problems when using local file resolutions
72 | package-lock.json
73 |
--------------------------------------------------------------------------------
/examples/gatsby-app/.prettierignore:
--------------------------------------------------------------------------------
1 | .cache
2 | package.json
3 | package-lock.json
4 | public
5 |
--------------------------------------------------------------------------------
/examples/gatsby-app/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "printWidth": 80
4 | }
5 |
--------------------------------------------------------------------------------
/examples/gatsby-app/README.md:
--------------------------------------------------------------------------------
1 | # Gatsby example
2 |
3 | This is an example of using `@auth0/auth0-react` with [Gatsby](https://www.gatsbyjs.org/).
4 |
5 | Follow the steps in [examples/README.md](../README.md) to setup an Auth0 application and API.
6 |
7 | Add the file `./examples/gatsby-app/.env.development` with the `domain` and `clientId` of the application and `audience` (your API identifier)
8 |
9 | ```dotenv
10 | GATSBY_DOMAIN=your-tenant.auth0.com
11 | GATSBY_CLIENT_ID=yourclientid
12 | GATSBY_AUDIENCE=https://api.example.com/users
13 | ```
14 |
15 | Run `npm start` to start the application at http://localhost:3000
16 |
17 | Start the API using the instructions in [examples/users-api/README.md](../users-api/README.md)
18 |
--------------------------------------------------------------------------------
/examples/gatsby-app/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | // gatsby-browser.js
2 | import React from 'react';
3 | import { Auth0Provider } from '@auth0/auth0-react';
4 | import { navigate } from 'gatsby';
5 | import 'bootstrap/dist/css/bootstrap.css';
6 | import './src/components/App.css';
7 |
8 | const onRedirectCallback = (appState) => navigate(appState?.returnTo || '/');
9 |
10 | export const wrapRootElement = ({ element }) => {
11 | return (
12 |
22 | {element}
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/examples/gatsby-app/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingSlash: 'never',
3 | };
4 |
--------------------------------------------------------------------------------
/examples/gatsby-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-starter-default",
3 | "private": true,
4 | "description": "A simple starter to get up and developing quickly with Gatsby",
5 | "version": "0.1.0",
6 | "dependencies": {
7 | "@auth0/auth0-react": "file:../..",
8 | "bootstrap": "^5.0.0",
9 | "gatsby": "^5.0.1",
10 | "react": "file:../../node_modules/react",
11 | "react-dom": "file:../../node_modules/react-dom"
12 | },
13 | "devDependencies": {
14 | "prettier": "2.0.5",
15 | "ajv": "8.16.0"
16 | },
17 | "keywords": [
18 | "gatsby"
19 | ],
20 | "license": "MIT",
21 | "scripts": {
22 | "build": "gatsby build",
23 | "develop": "gatsby develop -p 3000",
24 | "format": "prettier --write \"**/*.{js,jsx,json,md}\"",
25 | "start": "npm run develop",
26 | "serve": "gatsby serve",
27 | "clean": "gatsby clean",
28 | "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1"
29 | },
30 | "repository": {
31 | "type": "git",
32 | "url": "https://github.com/gatsbyjs/gatsby-starter-default"
33 | },
34 | "bugs": {
35 | "url": "https://github.com/gatsbyjs/gatsby/issues"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/examples/gatsby-app/src/components/App.css:
--------------------------------------------------------------------------------
1 | .spinner-border {
2 | top: 50%;
3 | position: fixed;
4 | margin-top: -1rem;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/gatsby-app/src/components/Error.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function Error({ message }) {
4 | return (
5 |
6 | Oops... {message}
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/examples/gatsby-app/src/components/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function Loading() {
4 | return (
5 |
6 |
7 | Loading...
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/examples/gatsby-app/src/components/Nav.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'gatsby';
3 | import { useAuth0 } from '@auth0/auth0-react';
4 |
5 | export function Nav() {
6 | const { isAuthenticated, user, loginWithRedirect, logout } = useAuth0();
7 | const pathname = typeof window !== 'undefined' && window.location.pathname;
8 |
9 | return (
10 |
11 |
12 |
@auth0/auth0-react
13 |
14 |
15 |
21 | Home
22 |
23 |
29 | Users
30 |
31 |
32 |
33 |
34 | {isAuthenticated ? (
35 |
36 | Hello, {user.name}! {' '}
37 |
41 | logout({ logoutParams: { returnTo: window.location.origin } })
42 | }
43 | >
44 | logout
45 |
46 |
47 | ) : (
48 |
loginWithRedirect()}
52 | >
53 | login
54 |
55 | )}
56 |
57 |
58 | );
59 | }
60 |
--------------------------------------------------------------------------------
/examples/gatsby-app/src/components/Users.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useApi } from '../hooks/use-api';
3 | import { Loading } from './Loading';
4 | import { Error } from './Error';
5 |
6 | const PORT = process.env.GATSBY_API_PORT || 3001;
7 |
8 | export function Users() {
9 | const {
10 | loading,
11 | error,
12 | data: users = [],
13 | } = useApi(`http://127.0.0.1:${PORT}/users`, {
14 | audience: process.env.GATSBY_AUDIENCE,
15 | scope: 'profile email read:users',
16 | });
17 |
18 | if (loading) {
19 | return ;
20 | }
21 |
22 | if (error) {
23 | return ;
24 | }
25 |
26 | return (
27 |
28 |
29 |
30 | Name
31 | Email
32 |
33 |
34 |
35 | {users.map(({ name, email }, i) => (
36 |
37 | {name}
38 | {email}
39 |
40 | ))}
41 |
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/examples/gatsby-app/src/hooks/use-api.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { useAuth0 } from '@auth0/auth0-react';
3 |
4 | export const useApi = (url, options) => {
5 | const { getAccessTokenSilently } = useAuth0();
6 | const [state, setState] = useState({
7 | error: null,
8 | loading: true,
9 | data: null,
10 | });
11 |
12 | useEffect(() => {
13 | (async () => {
14 | try {
15 | const { audience, scope, ...fetchOptions } = options;
16 | const accessToken = await getAccessTokenSilently({
17 | authorizationParams: { audience, scope },
18 | });
19 | const res = await fetch(url, {
20 | ...fetchOptions,
21 | headers: {
22 | ...fetchOptions.headers,
23 | // Add the Authorization header to the existing headers
24 | Authorization: `Bearer ${accessToken}`,
25 | },
26 | });
27 | setState({
28 | ...state,
29 | data: await res.json(),
30 | error: null,
31 | loading: false,
32 | });
33 | } catch (error) {
34 | setState({
35 | ...state,
36 | error,
37 | loading: false,
38 | });
39 | }
40 | })();
41 | }, []); // eslint-disable-line react-hooks/exhaustive-deps
42 |
43 | return state;
44 | };
45 |
--------------------------------------------------------------------------------
/examples/gatsby-app/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useAuth0 } from '@auth0/auth0-react';
3 | import { Nav } from '../components/Nav';
4 | import { Loading } from '../components/Loading';
5 | import { Error } from '../components/Error';
6 |
7 | const IndexPage = () => {
8 | const { isLoading, error } = useAuth0();
9 |
10 | if (isLoading) {
11 | return ;
12 | }
13 |
14 | return (
15 | <>
16 |
17 | {error && }
18 | >
19 | );
20 | };
21 |
22 | export default IndexPage;
23 |
--------------------------------------------------------------------------------
/examples/gatsby-app/src/pages/users.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withAuthenticationRequired } from '@auth0/auth0-react';
3 | import { Users } from '../components/Users';
4 | import { Nav } from '../components/Nav';
5 |
6 | const PORT = process.env.GATSBY_API_PORT || 3001;
7 |
8 | const UsersPage = () => {
9 | return (
10 | <>
11 |
12 |
13 | >
14 | );
15 | };
16 |
17 | export default withAuthenticationRequired(UsersPage);
18 |
--------------------------------------------------------------------------------
/examples/nextjs-app/.env.sample:
--------------------------------------------------------------------------------
1 | NEXT_PUBLIC_DOMAIN=your-tenant.auth0.com
2 | NEXT_PUBLIC_CLIENT_ID=yourclientid
3 | NEXT_PUBLIC_AUDIENCE=https://api.example.com/users
4 | NEXT_PUBLIC_API_PORT=3001
5 |
--------------------------------------------------------------------------------
/examples/nextjs-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 |
21 | # debug
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | # local env files
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
32 | # Intentionally removing package lock because it has problems when using local file resolutions
33 | package-lock.json
34 |
--------------------------------------------------------------------------------
/examples/nextjs-app/README.md:
--------------------------------------------------------------------------------
1 | # Next.js example
2 |
3 | This is an example of using `@auth0/auth0-react` with [Next.js](https://nextjs.org/).
4 |
5 | Follow the steps in [examples/README.md](../README.md) to setup an Auth0 application and API.
6 |
7 | Add the file `./examples/nextjs-app/.env` with the `domain` and `clientId` of the application and `audience` (your API identifier)
8 |
9 | ```dotenv
10 | NEXT_PUBLIC_DOMAIN=your-tenant.auth0.com
11 | NEXT_PUBLIC_CLIENT_ID=yourclientid
12 | NEXT_PUBLIC_AUDIENCE=https://api.example.com/users
13 | ```
14 |
15 | Run `npm run dev` to start the application at http://localhost:3000
16 |
17 | Start the API using the instructions in [examples/users-api/README.md](../users-api/README.md)
18 |
--------------------------------------------------------------------------------
/examples/nextjs-app/components/App.css:
--------------------------------------------------------------------------------
1 | .spinner-border {
2 | top: 50%;
3 | position: fixed;
4 | margin-top: -1rem;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/nextjs-app/components/Error.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function Error({ message }) {
4 | return (
5 |
6 | Oops... {message}
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/examples/nextjs-app/components/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function Loading() {
4 | return (
5 |
6 |
7 | Loading...
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/examples/nextjs-app/components/Nav.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Link from 'next/link';
3 | import { useAuth0 } from '@auth0/auth0-react';
4 | import { useRouter } from 'next/router';
5 | import { Loading } from './Loading';
6 |
7 | export function Nav() {
8 | const { isAuthenticated, isLoading, user, loginWithRedirect, logout } =
9 | useAuth0();
10 | const { pathname } = useRouter();
11 |
12 | if (isLoading) {
13 | return ;
14 | }
15 |
16 | return (
17 |
18 | @auth0/auth0-react
19 |
20 |
21 |
25 | Home
26 |
27 |
33 | Users
34 |
35 |
36 |
37 |
38 | {isAuthenticated ? (
39 |
40 | Hello, {user.name}! {' '}
41 |
45 | logout({ logoutParams: { returnTo: window.location.origin } })
46 | }
47 | >
48 | logout
49 |
50 |
51 | ) : (
52 | loginWithRedirect()}
56 | >
57 | login
58 |
59 | )}
60 |
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/examples/nextjs-app/hooks/use-api.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { useAuth0 } from '@auth0/auth0-react';
3 |
4 | export const useApi = (url, options) => {
5 | const { getAccessTokenSilently } = useAuth0();
6 | const [state, setState] = useState({
7 | error: null,
8 | loading: true,
9 | data: null,
10 | });
11 |
12 | useEffect(() => {
13 | (async () => {
14 | try {
15 | const { audience, scope, ...fetchOptions } = options;
16 | const accessToken = await getAccessTokenSilently({
17 | authorizationParams: { audience, scope },
18 | });
19 | const res = await fetch(url, {
20 | ...fetchOptions,
21 | headers: {
22 | ...fetchOptions.headers,
23 | // Add the Authorization header to the existing headers
24 | Authorization: `Bearer ${accessToken}`,
25 | },
26 | });
27 | setState({
28 | ...state,
29 | data: await res.json(),
30 | error: null,
31 | loading: false,
32 | });
33 | } catch (error) {
34 | setState({
35 | ...state,
36 | error,
37 | loading: false,
38 | });
39 | }
40 | })();
41 | }, []); // eslint-disable-line react-hooks/exhaustive-deps
42 |
43 | return state;
44 | };
45 |
--------------------------------------------------------------------------------
/examples/nextjs-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start"
9 | },
10 | "dependencies": {
11 | "@auth0/auth0-react": "file:../..",
12 | "next": "14.1.1",
13 | "react": "file:../../node_modules/react",
14 | "react-dom": "file:../../node_modules/react-dom"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/nextjs-app/pages/_app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import App from 'next/app';
3 | import Head from 'next/head';
4 | import Router from 'next/router';
5 | import { Auth0Provider } from '@auth0/auth0-react';
6 | import { Nav } from '../components/Nav';
7 | import '../components/App.css';
8 |
9 | const onRedirectCallback = (appState) => {
10 | Router.replace(appState?.returnTo || '/');
11 | };
12 |
13 | class MyApp extends App {
14 | render() {
15 | const { Component, pageProps } = this.props;
16 | return (
17 |
27 |
28 |
29 |
30 | );
31 | }
32 | }
33 |
34 | export default MyApp;
35 |
--------------------------------------------------------------------------------
/examples/nextjs-app/pages/_document.js:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document';
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/examples/nextjs-app/pages/index.js:
--------------------------------------------------------------------------------
1 | export default function Home() {
2 | return <>>;
3 | }
4 |
--------------------------------------------------------------------------------
/examples/nextjs-app/pages/users.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withAuthenticationRequired } from '@auth0/auth0-react';
3 | import { useApi } from '../hooks/use-api';
4 | import { Loading } from '../components/Loading';
5 | import { Error } from '../components/Error';
6 |
7 | const PORT = process.env.NEXT_PUBLIC_API_PORT || 3001;
8 |
9 | const Users = () => {
10 | const {
11 | loading,
12 | error,
13 | data: users = [],
14 | } = useApi(`http://localhost:${PORT}/users`, {
15 | audience: process.env.NEXT_PUBLIC_AUDIENCE,
16 | scope: 'profile email read:users',
17 | });
18 |
19 | if (loading) {
20 | return ;
21 | }
22 |
23 | if (error) {
24 | return ;
25 | }
26 |
27 | return (
28 |
29 |
30 |
31 | Name
32 | Email
33 |
34 |
35 |
36 | {users.map(({ name, email }, i) => (
37 |
38 | {name}
39 | {email}
40 |
41 | ))}
42 |
43 |
44 | );
45 | };
46 |
47 | export default withAuthenticationRequired(Users);
48 |
--------------------------------------------------------------------------------
/examples/users-api/.env.sample:
--------------------------------------------------------------------------------
1 | DOMAIN=your-tenant.auth0.com
2 | AUDIENCE=https://api.example.com/users
3 | PORT=3001
4 |
--------------------------------------------------------------------------------
/examples/users-api/README.md:
--------------------------------------------------------------------------------
1 | # User API
2 |
3 | This is a simple express app that has one API endpoint (`/users`) protected with Auth0.
4 |
5 | Follow the steps in [examples/README.md](../README.md) to setup an Auth0 application and API.
6 |
7 | Add a `.env` file to `./examples/users-api/.env` With the `domain` and `audience` (your API identifier)
8 |
9 | ```dotenv
10 | DOMAIN=your_domain
11 | AUDIENCE=your_audience
12 | ```
13 |
14 | Run `npm start` to start the API at http://localhost:3001
15 |
--------------------------------------------------------------------------------
/examples/users-api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "users-api",
3 | "private": true,
4 | "version": "1.0.0",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "license": "MIT",
10 | "dependencies": {
11 | "cors": "^2.8.5",
12 | "dotenv": "^16.0.3",
13 | "express": "^4.19.2",
14 | "express-oauth2-jwt-bearer": "^1.2.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/users-api/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const cors = require('cors');
3 | const dotenv = require('dotenv');
4 | const { auth, requiredScopes } = require('express-oauth2-jwt-bearer');
5 |
6 | dotenv.config();
7 |
8 | const { DOMAIN, AUDIENCE, PORT = 3001 } = process.env;
9 |
10 | const app = express();
11 |
12 | if (!DOMAIN || !AUDIENCE) {
13 | throw new Error(
14 | 'Please make sure that DOMAIN and AUDIENCE is set in your .env file'
15 | );
16 | }
17 |
18 | app.use(cors()); // Allow all cors (not recommended for production)
19 |
20 | const checkJwt = auth({
21 | audience: AUDIENCE,
22 | issuerBaseURL: `https://${DOMAIN}`,
23 | });
24 |
25 | app.head('/', (req, res) => res.send('ok'));
26 | app.get('/', (req, res) => res.status(200).send('OK'));
27 |
28 | app.get('/users', checkJwt, requiredScopes('read:users'), (req, res) => {
29 | res.send([
30 | { name: 'Bob', email: 'bob@example.com' },
31 | { name: 'Alice', email: 'alice@example.com' },
32 | ]);
33 | });
34 |
35 | app.listen(PORT, '0.0.0.0', () => console.log(`API Server listening on port ${PORT}`));
36 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | const pkg = require('./package.json');
2 |
3 | module.exports = {
4 | clearMocks: true,
5 | coveragePathIgnorePatterns: ['/__tests__/', 'index.tsx'],
6 | coverageReporters: ['lcov', 'text', 'text-summary'],
7 | preset: 'ts-jest',
8 | reporters: [
9 | 'default',
10 | ['jest-junit', { outputDirectory: 'test-results/jest' }],
11 | ],
12 | testEnvironment: 'jsdom',
13 | testEnvironmentOptions: {
14 | url: 'https://www.example.com/',
15 | },
16 | testRegex: '/__tests__/.+test.tsx?$',
17 | globals: {
18 | __VERSION__: pkg.version,
19 | },
20 | transform: {
21 | '^.+\\.tsx?$': [
22 | 'ts-jest',
23 | {
24 | tsconfig: {
25 | target: 'es6',
26 | },
27 | },
28 | ],
29 | },
30 | coverageThreshold: {
31 | global: {
32 | branches: 100,
33 | functions: 100,
34 | lines: 100,
35 | statements: 100,
36 | },
37 | },
38 | };
39 |
--------------------------------------------------------------------------------
/opslevel.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 1
3 | repository:
4 | owner: dx_sdks
5 | tier:
6 | tags:
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "Auth0",
3 | "name": "@auth0/auth0-react",
4 | "version": "2.3.0",
5 | "description": "Auth0 SDK for React Single Page Applications (SPA)",
6 | "keywords": [
7 | "auth0",
8 | "login",
9 | "Authorization Code Grant Flow",
10 | "PKCE",
11 | "Single Page Application authentication",
12 | "SPA authentication",
13 | "react"
14 | ],
15 | "files": [
16 | "src",
17 | "dist"
18 | ],
19 | "main": "dist/auth0-react.cjs.js",
20 | "types": "dist/index.d.ts",
21 | "module": "dist/auth0-react.esm.js",
22 | "scripts": {
23 | "build": "npm run lint && rollup -c --environment NODE_ENV:production",
24 | "lint": "eslint --ext=tsx ./src ./__tests__",
25 | "start": "rollup -cw",
26 | "test": "jest --coverage",
27 | "prepack": "npm run build",
28 | "docs": "typedoc --options typedoc.js src",
29 | "install:examples": "npm i --prefix=examples/cra-react-router --no-package-lock --legacy-peer-deps && npm i --prefix=examples/gatsby-app --no-package-lock --legacy-peer-deps && npm i --prefix=examples/nextjs-app --no-package-lock --legacy-peer-deps && npm ci --prefix=examples/users-api",
30 | "start:cra": "npm start --prefix=examples/cra-react-router",
31 | "start:gatsby": "npm start --prefix=examples/gatsby-app",
32 | "start:nextjs": "npm run dev --prefix=examples/nextjs-app",
33 | "start:api": "npm start --prefix=examples/users-api",
34 | "test:cra": "start-server-and-test start:api 3001 start:cra http-get://127.0.0.1:3000 cypress:run",
35 | "test:cra:watch": "start-server-and-test start:api 3001 start:cra 3000 cypress:open",
36 | "test:gatsby": "start-server-and-test start:api 3001 start:gatsby http-get://localhost:3000 cypress:run",
37 | "test:gatsby:watch": "start-server-and-test start:api 3001 start:gatsby 3000 cypress:open",
38 | "test:nextjs": "start-server-and-test start:api 3001 start:nextjs 3000 cypress:run",
39 | "test:nextjs:watch": "start-server-and-test start:api 3001 start:nextjs 3000 cypress:open",
40 | "test:integration": "npm run test:cra && npm run test:gatsby && npm run test:nextjs",
41 | "cypress:run": "cypress run --spec 'cypress/e2e/smoke.cy.ts'",
42 | "cypress:open": "cypress open"
43 | },
44 | "repository": {
45 | "type": "git",
46 | "url": "git+https://github.com/auth0/auth0-react.git"
47 | },
48 | "license": "MIT",
49 | "bugs": {
50 | "url": "https://github.com/auth0/auth0-react/issues"
51 | },
52 | "homepage": "https://github.com/auth0/auth0-react#readme",
53 | "devDependencies": {
54 | "@rollup/plugin-node-resolve": "^15.0.1",
55 | "@rollup/plugin-replace": "^5.0.1",
56 | "@rollup/plugin-terser": "^0.4.3",
57 | "@testing-library/jest-dom": "6.6.3",
58 | "@testing-library/react": "16.1.0",
59 | "@testing-library/dom": "^10.4.0",
60 | "@types/jest": "^29.2.3",
61 | "@types/react": "19.0.7",
62 | "@types/react-dom": "19.0.3",
63 | "@typescript-eslint/eslint-plugin": "^5.45.0",
64 | "@typescript-eslint/parser": "^5.45.0",
65 | "browserstack-cypress-cli": "^1.19.1",
66 | "cypress": "^13.1.0",
67 | "eslint": "^8.28.0",
68 | "eslint-plugin-react": "^7.31.11",
69 | "eslint-plugin-react-hooks": "^4.6.0",
70 | "husky": "^8.0.3",
71 | "jest": "^29.3.1",
72 | "jest-environment-jsdom": "^29.3.1",
73 | "jest-junit": "^15.0.0",
74 | "oidc-provider": "^8.0.0",
75 | "prettier": "^2.8.1",
76 | "pretty-quick": "^3.1.3",
77 | "react": "^19.0.0",
78 | "react-dom": "19.0.0",
79 | "rollup": "^3.7.0",
80 | "rollup-plugin-analyzer": "^4.0.0",
81 | "rollup-plugin-delete": "^2.0.0",
82 | "rollup-plugin-dev": "^1.1.3",
83 | "rollup-plugin-livereload": "^2.0.5",
84 | "rollup-plugin-peer-deps-external": "^2.2.4",
85 | "rollup-plugin-serve": "^2.0.2",
86 | "rollup-plugin-typescript2": "^0.36.0",
87 | "start-server-and-test": "^2.0",
88 | "ts-jest": "^29.0.3",
89 | "tslib": "^2.4.1",
90 | "typedoc": "^0.25.3",
91 | "typescript": "^4.9.4"
92 | },
93 | "peerDependencies": {
94 | "react": "^16.11.0 || ^17 || ^18 || ^19",
95 | "react-dom": "^16.11.0 || ^17 || ^18 || ^19"
96 | },
97 | "dependencies": {
98 | "@auth0/auth0-spa-js": "^2.1.3"
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import del from 'rollup-plugin-delete';
2 | import livereload from 'rollup-plugin-livereload';
3 | import dev from 'rollup-plugin-dev';
4 | import typescript from 'rollup-plugin-typescript2';
5 | import external from 'rollup-plugin-peer-deps-external';
6 | import terser from '@rollup/plugin-terser';
7 | import resolve from '@rollup/plugin-node-resolve';
8 | import replace from '@rollup/plugin-replace';
9 | import pkg from './package.json' assert { type: 'json' };
10 | import analyze from 'rollup-plugin-analyzer';
11 | import { createApp } from './scripts/oidc-provider.mjs';
12 |
13 | const isProduction = process.env.NODE_ENV === 'production';
14 | const name = 'reactAuth0';
15 | const input = 'src/index.tsx';
16 | const globals = {
17 | react: 'React',
18 | 'react-dom': 'ReactDOM',
19 | };
20 | const plugins = [
21 | del({ targets: 'dist/*', runOnce: true }),
22 | typescript({ useTsconfigDeclarationDir: true }),
23 | external(),
24 | resolve(),
25 | replace({ __VERSION__: `'${pkg.version}'`, preventAssignment: true }),
26 | analyze({ summaryOnly: true }),
27 | ];
28 |
29 | export default [
30 | {
31 | input,
32 | output: [
33 | {
34 | name,
35 | file: 'dist/auth0-react.js',
36 | format: 'umd',
37 | globals,
38 | sourcemap: true,
39 | },
40 | ],
41 | plugins: [
42 | ...plugins,
43 | ...(isProduction
44 | ? []
45 | : [
46 | dev({
47 | dirs: ['dist', 'static'],
48 | port: 3000,
49 | extend(app, modules) {
50 | app.use(modules.mount(createApp({ port: 3000 })));
51 | },
52 | }),
53 | livereload(),
54 | ]),
55 | ],
56 | },
57 | ...(isProduction
58 | ? [
59 | {
60 | input,
61 | output: [
62 | {
63 | name,
64 | file: 'dist/auth0-react.min.js',
65 | format: 'umd',
66 | globals,
67 | sourcemap: true,
68 | },
69 | ],
70 | plugins: [...plugins, terser()],
71 | },
72 | {
73 | input,
74 | output: {
75 | name,
76 | file: pkg.main,
77 | format: 'cjs',
78 | sourcemap: true,
79 | },
80 | plugins,
81 | },
82 | {
83 | input,
84 | output: {
85 | file: pkg.module,
86 | format: 'esm',
87 | sourcemap: true,
88 | },
89 | plugins,
90 | },
91 | ]
92 | : []),
93 | ];
94 |
--------------------------------------------------------------------------------
/scripts/oidc-provider.mjs:
--------------------------------------------------------------------------------
1 | import Provider, { interactionPolicy } from 'oidc-provider';
2 |
3 | const { base, Prompt, Check } = interactionPolicy;
4 |
5 | const policy = base();
6 | policy.add(
7 | new Prompt(
8 | { name: 'noop', requestable: false },
9 | new Check('foo', 'bar', (ctx) => {
10 | if (
11 | ctx.query &&
12 | ctx.query.scope &&
13 | ctx.query.scope.includes('offline_access')
14 | ) {
15 | ctx.oidc.params.scope = `${ctx.oidc.params.scope} offline_access`;
16 | }
17 | return Check.NO_NEED_TO_PROMPT;
18 | })
19 | ),
20 | 0
21 | );
22 |
23 | const config = {
24 | clients: [
25 | {
26 | client_id: 'testing',
27 | redirect_uris: ['http://127.0.0.1:3000'],
28 | token_endpoint_auth_method: 'none',
29 | grant_types: ['authorization_code', 'refresh_token'],
30 | },
31 | ],
32 | routes: {
33 | authorization: '/authorize', // lgtm [js/hardcoded-credentials]
34 | token: '/oauth/token',
35 | end_session: '/v2/logout',
36 | },
37 | scopes: ['openid', 'offline_access', 'profile'],
38 | clientBasedCORS(ctx, origin, client) {
39 | return true;
40 | },
41 | features: {
42 | webMessageResponseMode: {
43 | enabled: true,
44 | },
45 | },
46 | rotateRefreshToken: true,
47 | interactions: {
48 | policy,
49 | },
50 | conformIdTokenClaims: false,
51 | claims: {
52 | profile: ['name'],
53 | },
54 | async findAccount(ctx, id) {
55 | return {
56 | accountId: id,
57 | async claims() {
58 | return { sub: id, name: id };
59 | },
60 | };
61 | },
62 | };
63 |
64 | export function createApp(opts) {
65 | const issuer = `http://127.0.0.1:${opts.port || 3000}/`;
66 | const provider = new Provider(issuer, config);
67 |
68 | provider.use(async (ctx, next) => {
69 | await next();
70 |
71 | if (ctx.oidc && ctx.oidc.route === 'end_session_success') {
72 | ctx.redirect('http://127.0.0.1:3000');
73 | }
74 | });
75 |
76 | return provider.app;
77 | }
78 |
--------------------------------------------------------------------------------
/src/auth-state.tsx:
--------------------------------------------------------------------------------
1 | import { User } from '@auth0/auth0-spa-js';
2 |
3 | /**
4 | * The auth state which, when combined with the auth methods, make up the return object of the `useAuth0` hook.
5 | */
6 | export interface AuthState {
7 | error?: Error;
8 | isAuthenticated: boolean;
9 | isLoading: boolean;
10 | user?: TUser;
11 | }
12 |
13 | /**
14 | * The initial auth state.
15 | */
16 | export const initialAuthState: AuthState = {
17 | isAuthenticated: false,
18 | isLoading: true,
19 | };
20 |
--------------------------------------------------------------------------------
/src/auth0-context.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | GetTokenSilentlyOptions,
3 | GetTokenWithPopupOptions,
4 | IdToken,
5 | LogoutOptions as SPALogoutOptions,
6 | PopupLoginOptions,
7 | PopupConfigOptions,
8 | RedirectLoginResult,
9 | User,
10 | GetTokenSilentlyVerboseResponse,
11 | RedirectLoginOptions as SPARedirectLoginOptions,
12 | } from '@auth0/auth0-spa-js';
13 | import { createContext } from 'react';
14 | import { AuthState, initialAuthState } from './auth-state';
15 | import { AppState } from './auth0-provider';
16 |
17 | // eslint-disable-next-line @typescript-eslint/no-empty-interface
18 | export interface LogoutOptions extends Omit {}
19 | // eslint-disable-next-line @typescript-eslint/no-empty-interface
20 | export interface RedirectLoginOptions
21 | extends Omit, 'onRedirect'> {}
22 |
23 | /**
24 | * Contains the authenticated state and authentication methods provided by the `useAuth0` hook.
25 | */
26 | export interface Auth0ContextInterface
27 | extends AuthState {
28 | /**
29 | * ```js
30 | * const token = await getAccessTokenSilently(options);
31 | * ```
32 | *
33 | * If there's a valid token stored, return it. Otherwise, opens an
34 | * iframe with the `/authorize` URL using the parameters provided
35 | * as arguments. Random and secure `state` and `nonce` parameters
36 | * will be auto-generated. If the response is successful, results
37 | * will be valid according to their expiration times.
38 | *
39 | * If refresh tokens are used, the token endpoint is called directly with the
40 | * 'refresh_token' grant. If no refresh token is available to make this call,
41 | * the SDK will only fall back to using an iframe to the '/authorize' URL if
42 | * the `useRefreshTokensFallback` setting has been set to `true`. By default this
43 | * setting is `false`.
44 | *
45 | * This method may use a web worker to perform the token call if the in-memory
46 | * cache is used.
47 | *
48 | * If an `audience` value is given to this function, the SDK always falls
49 | * back to using an iframe to make the token exchange.
50 | *
51 | * Note that in all cases, falling back to an iframe requires access to
52 | * the `auth0` cookie.
53 | */
54 | getAccessTokenSilently: {
55 | (
56 | options: GetTokenSilentlyOptions & { detailedResponse: true }
57 | ): Promise;
58 | (options?: GetTokenSilentlyOptions): Promise;
59 | (options: GetTokenSilentlyOptions): Promise<
60 | GetTokenSilentlyVerboseResponse | string
61 | >;
62 | };
63 |
64 | /**
65 | * ```js
66 | * const token = await getTokenWithPopup(options, config);
67 | * ```
68 | *
69 | * Get an access token interactively.
70 | *
71 | * Opens a popup with the `/authorize` URL using the parameters
72 | * provided as arguments. Random and secure `state` and `nonce`
73 | * parameters will be auto-generated. If the response is successful,
74 | * results will be valid according to their expiration times.
75 | */
76 | getAccessTokenWithPopup: (
77 | options?: GetTokenWithPopupOptions,
78 | config?: PopupConfigOptions
79 | ) => Promise;
80 |
81 | /**
82 | * ```js
83 | * const claims = await getIdTokenClaims();
84 | * ```
85 | *
86 | * Returns all claims from the id_token if available.
87 | */
88 | getIdTokenClaims: () => Promise;
89 |
90 | /**
91 | * ```js
92 | * await loginWithRedirect(options);
93 | * ```
94 | *
95 | * Performs a redirect to `/authorize` using the parameters
96 | * provided as arguments. Random and secure `state` and `nonce`
97 | * parameters will be auto-generated.
98 | */
99 | loginWithRedirect: (
100 | options?: RedirectLoginOptions
101 | ) => Promise;
102 |
103 | /**
104 | * ```js
105 | * await loginWithPopup(options, config);
106 | * ```
107 | *
108 | * Opens a popup with the `/authorize` URL using the parameters
109 | * provided as arguments. Random and secure `state` and `nonce`
110 | * parameters will be auto-generated. If the response is successful,
111 | * results will be valid according to their expiration times.
112 | *
113 | * IMPORTANT: This method has to be called from an event handler
114 | * that was started by the user like a button click, for example,
115 | * otherwise the popup will be blocked in most browsers.
116 | */
117 | loginWithPopup: (
118 | options?: PopupLoginOptions,
119 | config?: PopupConfigOptions
120 | ) => Promise;
121 |
122 | /**
123 | * ```js
124 | * auth0.logout({ logoutParams: { returnTo: window.location.origin } });
125 | * ```
126 | *
127 | * Clears the application session and performs a redirect to `/v2/logout`, using
128 | * the parameters provided as arguments, to clear the Auth0 session.
129 | * If the `logoutParams.federated` option is specified, it also clears the Identity Provider session.
130 | * [Read more about how Logout works at Auth0](https://auth0.com/docs/logout).
131 | */
132 | logout: (options?: LogoutOptions) => Promise;
133 |
134 | /**
135 | * After the browser redirects back to the callback page,
136 | * call `handleRedirectCallback` to handle success and error
137 | * responses from Auth0. If the response is successful, results
138 | * will be valid according to their expiration times.
139 | *
140 | * @param url The URL to that should be used to retrieve the `state` and `code` values. Defaults to `window.location.href` if not given.
141 | */
142 | handleRedirectCallback: (url?: string) => Promise;
143 | }
144 |
145 | /**
146 | * @ignore
147 | */
148 | const stub = (): never => {
149 | throw new Error('You forgot to wrap your component in .');
150 | };
151 |
152 | /**
153 | * @ignore
154 | */
155 | export const initialContext = {
156 | ...initialAuthState,
157 | buildAuthorizeUrl: stub,
158 | buildLogoutUrl: stub,
159 | getAccessTokenSilently: stub,
160 | getAccessTokenWithPopup: stub,
161 | getIdTokenClaims: stub,
162 | loginWithRedirect: stub,
163 | loginWithPopup: stub,
164 | logout: stub,
165 | handleRedirectCallback: stub,
166 | };
167 |
168 | /**
169 | * The Auth0 Context
170 | */
171 | const Auth0Context = createContext(initialContext);
172 |
173 | export default Auth0Context;
174 |
--------------------------------------------------------------------------------
/src/errors.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * An OAuth2 error will come from the authorization server and will have at least an `error` property which will
3 | * be the error code. And possibly an `error_description` property
4 | *
5 | * See: https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.6
6 | */
7 | export class OAuthError extends Error {
8 | constructor(public error: string, public error_description?: string) {
9 | super(error_description || error);
10 |
11 | // https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
12 | Object.setPrototypeOf(this, OAuthError.prototype);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | export {
2 | default as Auth0Provider,
3 | Auth0ProviderOptions,
4 | AppState,
5 | } from './auth0-provider';
6 | export { default as useAuth0 } from './use-auth0';
7 | export { default as withAuth0, WithAuth0Props } from './with-auth0';
8 | export {
9 | default as withAuthenticationRequired,
10 | WithAuthenticationRequiredOptions,
11 | } from './with-authentication-required';
12 | export {
13 | default as Auth0Context,
14 | Auth0ContextInterface,
15 | initialContext,
16 | LogoutOptions,
17 | RedirectLoginOptions,
18 | } from './auth0-context';
19 | export {
20 | AuthorizationParams,
21 | PopupLoginOptions,
22 | PopupConfigOptions,
23 | GetTokenWithPopupOptions,
24 | LogoutUrlOptions,
25 | CacheLocation,
26 | GetTokenSilentlyOptions,
27 | IdToken,
28 | User,
29 | ICache,
30 | InMemoryCache,
31 | LocalStorageCache,
32 | Cacheable,
33 | TimeoutError,
34 | MfaRequiredError,
35 | PopupCancelledError,
36 | PopupTimeoutError,
37 | AuthenticationError,
38 | MissingRefreshTokenError,
39 | GenericError
40 | } from '@auth0/auth0-spa-js';
41 | export { OAuthError } from './errors';
42 |
--------------------------------------------------------------------------------
/src/reducer.tsx:
--------------------------------------------------------------------------------
1 | import { User } from '@auth0/auth0-spa-js';
2 | import { AuthState } from './auth-state';
3 |
4 | type Action =
5 | | { type: 'LOGIN_POPUP_STARTED' }
6 | | {
7 | type:
8 | | 'INITIALISED'
9 | | 'LOGIN_POPUP_COMPLETE'
10 | | 'GET_ACCESS_TOKEN_COMPLETE'
11 | | 'HANDLE_REDIRECT_COMPLETE';
12 | user?: User;
13 | }
14 | | { type: 'LOGOUT' }
15 | | { type: 'ERROR'; error: Error };
16 |
17 | /**
18 | * Handles how that state changes in the `useAuth0` hook.
19 | */
20 | export const reducer = (state: AuthState, action: Action): AuthState => {
21 | switch (action.type) {
22 | case 'LOGIN_POPUP_STARTED':
23 | return {
24 | ...state,
25 | isLoading: true,
26 | };
27 | case 'LOGIN_POPUP_COMPLETE':
28 | case 'INITIALISED':
29 | return {
30 | ...state,
31 | isAuthenticated: !!action.user,
32 | user: action.user,
33 | isLoading: false,
34 | error: undefined,
35 | };
36 | case 'HANDLE_REDIRECT_COMPLETE':
37 | case 'GET_ACCESS_TOKEN_COMPLETE':
38 | if (state.user === action.user) {
39 | return state;
40 | }
41 | return {
42 | ...state,
43 | isAuthenticated: !!action.user,
44 | user: action.user,
45 | };
46 | case 'LOGOUT':
47 | return {
48 | ...state,
49 | isAuthenticated: false,
50 | user: undefined,
51 | };
52 | case 'ERROR':
53 | return {
54 | ...state,
55 | isLoading: false,
56 | error: action.error,
57 | };
58 | }
59 | };
60 |
--------------------------------------------------------------------------------
/src/use-auth0.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 | import { User } from '@auth0/auth0-spa-js';
3 | import Auth0Context, { Auth0ContextInterface } from './auth0-context';
4 |
5 | /**
6 | * ```js
7 | * const {
8 | * // Auth state:
9 | * error,
10 | * isAuthenticated,
11 | * isLoading,
12 | * user,
13 | * // Auth methods:
14 | * getAccessTokenSilently,
15 | * getAccessTokenWithPopup,
16 | * getIdTokenClaims,
17 | * loginWithRedirect,
18 | * loginWithPopup,
19 | * logout,
20 | * } = useAuth0();
21 | * ```
22 | *
23 | * Use the `useAuth0` hook in your components to access the auth state and methods.
24 | *
25 | * TUser is an optional type param to provide a type to the `user` field.
26 | */
27 | const useAuth0 = (
28 | context = Auth0Context
29 | ): Auth0ContextInterface =>
30 | useContext(context) as Auth0ContextInterface;
31 |
32 | export default useAuth0;
33 |
--------------------------------------------------------------------------------
/src/utils.tsx:
--------------------------------------------------------------------------------
1 | import { OAuthError } from './errors';
2 |
3 | const CODE_RE = /[?&]code=[^&]+/;
4 | const STATE_RE = /[?&]state=[^&]+/;
5 | const ERROR_RE = /[?&]error=[^&]+/;
6 |
7 | export const hasAuthParams = (searchParams = window.location.search): boolean =>
8 | (CODE_RE.test(searchParams) || ERROR_RE.test(searchParams)) &&
9 | STATE_RE.test(searchParams);
10 |
11 | const normalizeErrorFn =
12 | (fallbackMessage: string) =>
13 | (error: unknown): Error => {
14 | if (error instanceof Error) {
15 | return error;
16 | }
17 | // try to check errors of the following form: {error: string; error_description?: string}
18 | if (
19 | error !== null &&
20 | typeof error === 'object' &&
21 | 'error' in error &&
22 | typeof error.error === 'string'
23 | ) {
24 | if (
25 | 'error_description' in error &&
26 | typeof error.error_description === 'string'
27 | ) {
28 | return new OAuthError(error.error, error.error_description);
29 | }
30 | return new OAuthError(error.error);
31 | }
32 | return new Error(fallbackMessage);
33 | };
34 |
35 | export const loginError = normalizeErrorFn('Login failed');
36 |
37 | export const tokenError = normalizeErrorFn('Get access token failed');
38 |
39 | /**
40 | * @ignore
41 | * Helper function to map the v1 `redirectUri` option to the v2 `authorizationParams.redirect_uri`
42 | * and log a warning.
43 | */
44 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
45 | export const deprecateRedirectUri = (options?: any) => {
46 | if (options?.redirectUri) {
47 | console.warn(
48 | 'Using `redirectUri` has been deprecated, please use `authorizationParams.redirect_uri` instead as `redirectUri` will be no longer supported in a future version'
49 | );
50 | options.authorizationParams = options.authorizationParams || {};
51 | options.authorizationParams.redirect_uri = options.redirectUri;
52 | delete options.redirectUri;
53 | }
54 |
55 | if (options?.authorizationParams?.redirectUri) {
56 | console.warn(
57 | 'Using `authorizationParams.redirectUri` has been deprecated, please use `authorizationParams.redirect_uri` instead as `authorizationParams.redirectUri` will be removed in a future version'
58 | );
59 | options.authorizationParams.redirect_uri =
60 | options.authorizationParams.redirectUri;
61 | delete options.authorizationParams.redirectUri;
62 | }
63 | };
64 |
--------------------------------------------------------------------------------
/src/with-auth0.tsx:
--------------------------------------------------------------------------------
1 | import React, { ComponentType } from 'react';
2 | import Auth0Context, { Auth0ContextInterface } from './auth0-context';
3 |
4 | /**
5 | * Components wrapped in `withAuth0` will have an additional `auth0` prop
6 | */
7 | export interface WithAuth0Props {
8 | auth0: Auth0ContextInterface;
9 | }
10 |
11 | /**
12 | * ```jsx
13 | * class MyComponent extends Component {
14 | * render() {
15 | * // Access the auth context from the `auth0` prop
16 | * const { user } = this.props.auth0;
17 | * return Hello {user.name}!
18 | * }
19 | * }
20 | * // Wrap your class component in withAuth0
21 | * export default withAuth0(MyComponent);
22 | * ```
23 | *
24 | * Wrap your class components in this Higher Order Component to give them access to the Auth0Context.
25 | *
26 | * Providing a context as the second argument allows you to configure the Auth0Provider the Auth0Context
27 | * should come from f you have multiple within your application.
28 | */
29 | const withAuth0 = (
30 | Component: ComponentType
,
31 | context = Auth0Context
32 | ): ComponentType> => {
33 | return function WithAuth(props): React.JSX.Element {
34 | return (
35 |
36 | {(auth: Auth0ContextInterface): React.JSX.Element => (
37 |
38 | )}
39 |
40 | );
41 | };
42 | };
43 |
44 | export default withAuth0;
45 |
--------------------------------------------------------------------------------
/src/with-authentication-required.tsx:
--------------------------------------------------------------------------------
1 | import React, { ComponentType, useEffect, FC } from 'react';
2 | import useAuth0 from './use-auth0';
3 | import Auth0Context, {
4 | Auth0ContextInterface,
5 | RedirectLoginOptions,
6 | } from './auth0-context';
7 |
8 | /**
9 | * @ignore
10 | */
11 | const defaultOnRedirecting = (): React.JSX.Element => <>>;
12 |
13 | /**
14 | * @ignore
15 | */
16 | const defaultOnBeforeAuthentication = async (): Promise => {/* noop */ };
17 |
18 | /**
19 | * @ignore
20 | */
21 | const defaultReturnTo = (): string =>
22 | `${window.location.pathname}${window.location.search}`;
23 |
24 | /**
25 | * Options for the withAuthenticationRequired Higher Order Component
26 | */
27 | export interface WithAuthenticationRequiredOptions {
28 | /**
29 | * ```js
30 | * withAuthenticationRequired(Profile, {
31 | * returnTo: '/profile'
32 | * })
33 | * ```
34 | *
35 | * or
36 | *
37 | * ```js
38 | * withAuthenticationRequired(Profile, {
39 | * returnTo: () => window.location.hash.substr(1)
40 | * })
41 | * ```
42 | *
43 | * Add a path for the `onRedirectCallback` handler to return the user to after login.
44 | */
45 | returnTo?: string | (() => string);
46 | /**
47 | * ```js
48 | * withAuthenticationRequired(Profile, {
49 | * onRedirecting: () => Redirecting you to the login...
50 | * })
51 | * ```
52 | *
53 | * Render a message to show that the user is being redirected to the login.
54 | */
55 | onRedirecting?: () => React.JSX.Element;
56 | /**
57 | * ```js
58 | * withAuthenticationRequired(Profile, {
59 | * onBeforeAuthentication: () => { analyticsLibrary.track('login_triggered'); }
60 | * })
61 | * ```
62 | *
63 | * Allows executing logic before the user is redirected to the login page.
64 | */
65 | onBeforeAuthentication?: () => Promise;
66 | /**
67 | * ```js
68 | * withAuthenticationRequired(Profile, {
69 | * loginOptions: {
70 | * appState: {
71 | * customProp: 'foo'
72 | * }
73 | * }
74 | * })
75 | * ```
76 | *
77 | * Pass additional login options, like extra `appState` to the login page.
78 | * This will be merged with the `returnTo` option used by the `onRedirectCallback` handler.
79 | */
80 | loginOptions?: RedirectLoginOptions;
81 | /**
82 | * The context to be used when calling useAuth0, this should only be provided if you are using multiple Auth0Providers
83 | * within your application and you wish to tie a specific component to a Auth0Provider other than the Auth0Provider
84 | * associated with the default Auth0Context.
85 | */
86 | context?: React.Context;
87 | }
88 |
89 | /**
90 | * ```js
91 | * const MyProtectedComponent = withAuthenticationRequired(MyComponent);
92 | * ```
93 | *
94 | * When you wrap your components in this Higher Order Component and an anonymous user visits your component
95 | * they will be redirected to the login page; after login they will be returned to the page they were redirected from.
96 | */
97 | const withAuthenticationRequired = (
98 | Component: ComponentType
,
99 | options: WithAuthenticationRequiredOptions = {}
100 | ): FC
=> {
101 | return function WithAuthenticationRequired(props: P): React.JSX.Element {
102 | const {
103 | returnTo = defaultReturnTo,
104 | onRedirecting = defaultOnRedirecting,
105 | onBeforeAuthentication = defaultOnBeforeAuthentication,
106 | loginOptions,
107 | context = Auth0Context,
108 | } = options;
109 |
110 | const { isAuthenticated, isLoading, loginWithRedirect } =
111 | useAuth0(context);
112 |
113 | useEffect(() => {
114 | if (isLoading || isAuthenticated) {
115 | return;
116 | }
117 | const opts = {
118 | ...loginOptions,
119 | appState: {
120 | ...(loginOptions && loginOptions.appState),
121 | returnTo: typeof returnTo === 'function' ? returnTo() : returnTo,
122 | },
123 | };
124 | (async (): Promise => {
125 | await onBeforeAuthentication();
126 | await loginWithRedirect(opts);
127 | })();
128 | }, [
129 | isLoading,
130 | isAuthenticated,
131 | loginWithRedirect,
132 | onBeforeAuthentication,
133 | loginOptions,
134 | returnTo,
135 | ]);
136 |
137 | return isAuthenticated ? : onRedirecting();
138 | };
139 | };
140 |
141 | export default withAuthenticationRequired;
142 |
--------------------------------------------------------------------------------
/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 | Auth0 React
11 |
12 |
13 |
14 |
15 |
16 |
20 |
24 |
25 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["es6", "dom"],
5 | "jsx": "react",
6 | "declaration": true,
7 | "declarationDir": "./dist",
8 | "declarationMap": true,
9 | "sourceMap": true,
10 | "strict": true,
11 | "noUnusedLocals": true,
12 | "noUnusedParameters": true,
13 | "noImplicitReturns": true,
14 | "noFallthroughCasesInSwitch": true,
15 | "esModuleInterop": true,
16 | "resolveJsonModule": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "moduleResolution": "NodeNext"
19 | },
20 | "include": ["src"]
21 | }
22 |
--------------------------------------------------------------------------------
/typedoc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: '@auth0/auth0-react',
3 | out: './docs/',
4 | exclude: ['./src/utils.tsx', './src/reducer.tsx'],
5 | excludeExternals: false,
6 | excludePrivate: true,
7 | hideGenerator: true,
8 | readme: './README.md',
9 | visibilityFilters: {
10 | protected: false,
11 | inherited: true,
12 | external: true,
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
Provides the Auth0Context to its child components.
4 |