├── .version ├── .prettierignore ├── codecov.yml ├── src ├── version.ts ├── index.cjs.ts ├── interfaces │ ├── index.ts │ ├── app-state.ts │ ├── auth0-vue-client-options.ts │ └── auth0-plugin-options.ts ├── token.ts ├── global.ts ├── index.ts ├── utils.ts ├── guard.ts └── plugin.ts ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── Feature Request.yml │ └── Bug Report.yml ├── dependabot.yml ├── workflows │ ├── claude-code-review.yml │ ├── snyk.yml │ ├── codeql.yml │ ├── release.yml │ ├── browserstack.yml │ ├── rl-secure.yml │ ├── npm-release.yml │ └── test.yml ├── actions │ ├── get-version │ │ └── action.yml │ ├── build │ │ └── action.yml │ ├── get-prerelease │ │ └── action.yml │ ├── tag-exists │ │ └── action.yml │ ├── release-create │ │ └── action.yml │ ├── npm-publish │ │ └── action.yml │ ├── get-release-notes │ │ └── action.yml │ └── rl-scanner │ │ └── action.yml ├── stale.yml └── PULL_REQUEST_TEMPLATE.md ├── Jenkinsfile ├── opslevel.yml ├── tutorial ├── create-api.png ├── universal-login.png └── vue2-api.md ├── cypress ├── fixtures │ ├── profile.json │ ├── example.json │ └── users.json ├── tsconfig.json ├── support │ ├── commands.js │ └── index.js ├── plugins │ └── index.js └── integration │ └── test.js ├── .semgrepignore ├── .prettierrc ├── docs ├── .nojekyll ├── assets │ ├── navigation.js │ ├── search.js │ ├── highlight.css │ └── icons.svg ├── types │ ├── Cacheable.html │ ├── CacheLocation.html │ ├── CustomFetchMinimalOutput.html │ └── FetcherConfig.html ├── hierarchy.html ├── variables │ └── AUTH0_INJECTION_KEY.html ├── functions │ ├── useAuth0.html │ ├── authGuard.html │ └── createAuth0.html ├── interfaces │ ├── AppState.html │ ├── PopupLoginOptions.html │ └── PopupConfigOptions.html └── classes │ └── InMemoryCache.html ├── .npmignore ├── .shiprc ├── tsconfig.test.json ├── jest.setup.js ├── playground ├── api.ts ├── components │ ├── Error.vue │ └── Profile.vue ├── App.vue ├── auth0.ts ├── index.ts └── router.ts ├── .gitignore ├── .vscode ├── settings.json └── launch.json ├── .eslintrc.security ├── scripts ├── exec.js ├── print-bundle-size.js └── oidc-provider.js ├── .eslintrc ├── cypress.config.js ├── typedoc.js ├── tsconfig.json ├── static └── index.html ├── jest.config.js ├── browserstack.json ├── CONTRIBUTING.md ├── DEVELOPMENT.md ├── rollup.config.js ├── package.json ├── README.md └── __tests__ └── guard.test.ts /.version: -------------------------------------------------------------------------------- 1 | v2.5.0 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | docs -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | -------------------------------------------------------------------------------- /src/version.ts: -------------------------------------------------------------------------------- 1 | export default '2.3.1'; 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @auth0/project-dx-sdks-engineer-codeowner 2 | -------------------------------------------------------------------------------- /src/index.cjs.ts: -------------------------------------------------------------------------------- 1 | import * as Auth0Vue from './index'; 2 | 3 | export default Auth0Vue; 4 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | @Library('k8sAgents') agentLibrary 2 | @Library('auth0') _ 3 | SDKDeployment() 4 | -------------------------------------------------------------------------------- /opslevel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1 3 | repository: 4 | owner: dx_sdks 5 | tier: 6 | tags: 7 | -------------------------------------------------------------------------------- /tutorial/create-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/auth0-vue/HEAD/tutorial/create-api.png -------------------------------------------------------------------------------- /tutorial/universal-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/auth0-vue/HEAD/tutorial/universal-login.png -------------------------------------------------------------------------------- /cypress/fixtures/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 8739, 3 | "name": "Jane", 4 | "email": "jane@example.com" 5 | } 6 | -------------------------------------------------------------------------------- /.semgrepignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | __tests__/ 3 | playground/ 4 | tutorial/ 5 | static/ 6 | CHANGELOG.md 7 | README.md 8 | EXAMPLES.md 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 80, 4 | "trailingComma": "none", 5 | "arrowParens": "avoid" 6 | } 7 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | src 3 | test 4 | static 5 | .gitignore 6 | .npmignore 7 | rollup.config.js 8 | tsconfig.json 9 | .rpt2_cache 10 | .vscode 11 | -------------------------------------------------------------------------------- /src/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app-state'; 2 | export * from './auth0-plugin-options'; 3 | export * from './auth0-vue-client'; 4 | export * from './auth0-vue-client-options'; 5 | -------------------------------------------------------------------------------- /.shiprc: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "src/version.ts": [], 4 | ".version": [], 5 | "README.md": [ 6 | "{MAJOR}.{MINOR}" 7 | ] 8 | }, 9 | "postbump": "npm run docs" 10 | } -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "noImplicitAny": false, 5 | "target": "es6", 6 | "verbatimModuleSyntax": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | // Polyfills for Node.js test environment 2 | const { TextEncoder, TextDecoder } = require('util'); 3 | 4 | global.TextEncoder ??= TextEncoder; 5 | global.TextDecoder ??= TextDecoder; 6 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /playground/api.ts: -------------------------------------------------------------------------------- 1 | import type { GetTokenSilentlyOptions } from '@auth0/auth0-spa-js'; 2 | import { auth0 } from './auth0'; 3 | 4 | export async function getAccessTokenSilentlyOutsideComponent( 5 | options: GetTokenSilentlyOptions 6 | ) { 7 | return auth0.getAccessTokenSilently(options); 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .rpt2_cache 2 | node_modules 3 | dist 4 | coverage 5 | stats.html 6 | cypress/screenshots 7 | cypress/videos 8 | cypress.env.json 9 | .release 10 | .idea 11 | test-results 12 | yarn.lock 13 | .DS_Store 14 | release-tmp* 15 | bundle-stats 16 | 17 | # BrowserStack 18 | log/ 19 | *.log 20 | results/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "search.exclude": { 4 | "**/node_modules": true, 5 | "**/bower_components": true, 6 | "**/dist": true, 7 | "**/.rpt2_cache": true, 8 | "**/docs": true 9 | }, 10 | "typescript.tsdk": "node_modules/typescript/lib" 11 | } 12 | -------------------------------------------------------------------------------- /.eslintrc.security: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es2017": true, 6 | "jest": true, 7 | }, 8 | "extends": [ "plugin:security/recommended" ], 9 | "parser": "@typescript-eslint/parser", 10 | "parserOptions": { 11 | "project": "tsconfig.json" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scripts/exec.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').exec; 2 | 3 | module.exports = cmd => { 4 | return new Promise((resolve, reject) => { 5 | exec(cmd, (error, stdout, stderr) => { 6 | if (error) { 7 | reject(error); 8 | } 9 | resolve(stdout ? stdout : stderr); 10 | }); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: FAQ 4 | url: https://github.com/auth0/auth0-vue/blob/main/FAQ.md 5 | about: Read the FAQ to get answers to common issues 6 | - name: SDK API Documentation 7 | url: https://auth0.github.io/auth0-vue/ 8 | about: Read the API documentation for this SDK 9 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "target": "es5", 3 | "allowSyntheticDefaultImports": true, 4 | "esModuleInterop": true, 5 | "moduleResolution": "node", 6 | "compilerOptions": { 7 | "strict": true, 8 | "baseUrl": "../node_modules", 9 | "types": ["cypress", "node"], 10 | "noImplicitAny": false 11 | }, 12 | "include": ["**/*.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es2017": true, 6 | "jest": true 7 | }, 8 | "extends": [ 9 | "plugin:@typescript-eslint/recommended", 10 | "plugin:security/recommended" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "project": "tsconfig.json" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('cypress') 2 | 3 | module.exports = defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://127.0.0.1:3000', 6 | specPattern: 'cypress/integration/**/*.js', 7 | supportFile: 'cypress/support/index.js', 8 | viewportWidth: 1000, 9 | viewportHeight: 1000, 10 | chromeWebSecurity: false, 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /src/interfaces/app-state.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface AppState { 3 | /** 4 | * Target path the app gets routed to after 5 | * handling the callback from Auth0 (defaults to '/') 6 | */ 7 | target?: string; 8 | 9 | /** 10 | * Any custom parameter to be stored in appState 11 | */ 12 | [key: string]: any; 13 | } 14 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | -------------------------------------------------------------------------------- /src/token.ts: -------------------------------------------------------------------------------- 1 | import type { InjectionKey } from 'vue'; 2 | import type { Auth0VueClient } from './interfaces'; 3 | 4 | /** 5 | * @ignore 6 | */ 7 | export const AUTH0_TOKEN = '$auth0'; 8 | 9 | /** 10 | * Injection token used to `provide` the `Auth0VueClient` instance. Can be used to pass to `inject()` 11 | * 12 | * ```js 13 | * inject(AUTH0_INJECTION_KEY) 14 | * ``` 15 | */ 16 | export const AUTH0_INJECTION_KEY: InjectionKey = 17 | Symbol(AUTH0_TOKEN); 18 | -------------------------------------------------------------------------------- /.github/workflows/claude-code-review.yml: -------------------------------------------------------------------------------- 1 | name: Claude Code PR Review 2 | 3 | on: 4 | issue_comment: 5 | types: [ created ] 6 | pull_request_review_comment: 7 | types: [ created ] 8 | pull_request_review: 9 | types: [ submitted ] 10 | 11 | jobs: 12 | claude-review: 13 | permissions: 14 | contents: write 15 | issues: write 16 | pull-requests: write 17 | id-token: write 18 | uses: auth0/auth0-ai-pr-analyzer-gh-action/.github/workflows/claude-code-review.yml@main -------------------------------------------------------------------------------- /playground/components/Error.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /typedoc.js: -------------------------------------------------------------------------------- 1 | const excludeFiles = []; 2 | 3 | module.exports = { 4 | entryPoints: ['./src/index.ts'], // Changed from: includes: './src' 5 | out: './docs/', 6 | readme: './README.MD', 7 | exclude: [ 8 | '**/__tests__/**/*', 9 | '**/cypress/**/*', 10 | '**/__mocks__/**/*', 11 | ...excludeFiles.map(f => `./src/${f}.ts`) 12 | ], 13 | excludeExternals: false, 14 | excludePrivate: true, 15 | hideGenerator: true, 16 | visibilityFilters: { 17 | protected: false, 18 | inherited: true, 19 | external: true 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "sourceMap": true, 7 | "declaration": true, 8 | "declarationDir": "./dist/typings", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "downlevelIteration": true, 12 | "strict": true, 13 | "verbatimModuleSyntax": true 14 | }, 15 | "exclude": [ 16 | "./__tests__", 17 | "./dist/typings", 18 | "**/__mocks__/**", 19 | "./playground" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/global.ts: -------------------------------------------------------------------------------- 1 | export { Auth0Plugin } from './plugin'; 2 | export * from './interfaces'; 3 | export * from './guard'; 4 | 5 | export { 6 | User, 7 | InMemoryCache, 8 | LocalStorageCache, 9 | UseDpopNonceError 10 | } from '@auth0/auth0-spa-js'; 11 | 12 | export type { 13 | AuthorizationParams, 14 | PopupLoginOptions, 15 | PopupConfigOptions, 16 | GetTokenWithPopupOptions, 17 | LogoutUrlOptions, 18 | CacheLocation, 19 | GetTokenSilentlyOptions, 20 | IdToken, 21 | ICache, 22 | Cacheable, 23 | FetcherConfig, 24 | Fetcher, 25 | CustomFetchMinimalOutput 26 | } from '@auth0/auth0-spa-js'; 27 | -------------------------------------------------------------------------------- /playground/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 27 | -------------------------------------------------------------------------------- /src/interfaces/auth0-vue-client-options.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-empty-interface */ 2 | import type { 3 | Auth0ClientOptions, 4 | LogoutOptions as SPALogoutOptions, 5 | RedirectLoginOptions as SPARedirectLoginOptions 6 | } from '@auth0/auth0-spa-js'; 7 | import type { AppState } from './app-state'; 8 | 9 | /** 10 | * Configuration for the Auth0 Vue Client 11 | */ 12 | export interface Auth0VueClientOptions extends Auth0ClientOptions {} 13 | 14 | export interface LogoutOptions extends Omit {} 15 | export interface RedirectLoginOptions 16 | extends Omit, 'onRedirect'> {} 17 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Auth0 5 | 6 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example plugins/index.js can be used to load plugins 3 | // 4 | // You can change the location of this file or turn off loading 5 | // the plugins file with the 'pluginsFile' configuration option. 6 | // 7 | // You can read more here: 8 | // https://on.cypress.io/plugins-guide 9 | // *********************************************************** 10 | 11 | // This function is called when a project is opened or re-opened (e.g. due to 12 | // the project's config changing) 13 | 14 | module.exports = (on, config) => { 15 | // `on` is used to hook into various events Cypress emits 16 | // `config` is the resolved Cypress config 17 | }; 18 | -------------------------------------------------------------------------------- /docs/assets/navigation.js: -------------------------------------------------------------------------------- 1 | window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE6WWXW/aMBSG/4uv6ULZ2m3cVYx12SigAZuqaqq85ECsOrZlH0+l0/77RCiQxPmSuT5PnvfEPrL98JcgPCMZks+AUQKa9IiimJAhiTg1BkzwWniTYMpJjzwxEZPh5eBDb0+QIUETXzBzAc8IWlBO/vWO1lDcQSr1dkSjBFx3oeyXMJER5QuUmm6gJsVB/JJWBj4pqaZSRDDWWlaslYN4J1XLPX03Si2QYm5pmEDQaxqBCQ7FonpwdZ0XWEz6c243TMwUMilMtcrBWqU/LIw4A4H1wiPSXdbaZJlsU99aquM2ax5qE0rNXuiOnFNN03pniXO0nfb/FnApn0AsGAeBfNv0IzXsecE/GSZzqazqklyG/aLD0mGQCworDoHO2jjrsdq7r/mJJ3IjbePgFoim+dqDK83bbSfIr+tsn0ZSrNmmKczFzoibyJZjyKH8wr5DzDRE2JpXBTbtTzZ+9DfPTSduFZjgWCh93f/4/vJq0KnpTLG78HZdVPoPxTMyrEGZZu+COyZYSvnMorLoxNVw/smvb5H9JJXjCkX/jJvV8kv/MZx+HY+W4Wz6+G18f0r6QzXbbZAJKrBi5ttBTkoP18NJtbYiykYlOBaLgut3OUGkgSJkd1eVIlfuJKntpYQ0yKypb+dQcz7/9R+Lfmw5cQoAAA==" -------------------------------------------------------------------------------- /scripts/print-bundle-size.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const Table = require('cli-table'); 3 | const gzipSize = require('gzip-size'); 4 | 5 | const toKb = b => `${(b / Math.pow(1024, 1)).toFixed(2)} kb`; 6 | 7 | const table = new Table({ 8 | head: ['File', 'Size', 'GZIP size'] 9 | }); 10 | 11 | if (!fs.existsSync('./dist')) { 12 | console.log(`Can't print bundle size because ./dist doesn't exist.`); 13 | return; 14 | } 15 | 16 | fs.readdirSync('./dist') 17 | .filter(f => f.endsWith('.js')) 18 | .forEach(f => { 19 | const path = `./dist/${f}`; 20 | table.push([ 21 | f, 22 | toKb(fs.statSync(path).size), 23 | toKb(gzipSize.fileSync(path)) 24 | ]); 25 | }); 26 | 27 | console.log(table.toString()); 28 | -------------------------------------------------------------------------------- /.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: 22 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 23 | 24 | - name: Build package 25 | shell: bash 26 | run: npm run build 27 | env: 28 | WITH_STATS: true 29 | 30 | - name: Get bundle size 31 | shell: bash 32 | run: npm run print-bundle-size 33 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /playground/auth0.ts: -------------------------------------------------------------------------------- 1 | import { createAuth0 } from '../src/index'; 2 | 3 | const defaultDomain = 'http://127.0.0.1:3000'; 4 | const defaultClientId = 'testing'; 5 | const defaultAudience = 'Test'; 6 | 7 | const res = localStorage.getItem('vue-playground-data') 8 | ? JSON.parse(localStorage.getItem('vue-playground-data') as string) 9 | : undefined; 10 | const domain = res?.domain || defaultDomain; 11 | const clientId = res?.client_id || defaultClientId; 12 | const audience = res?.audience || defaultAudience; 13 | 14 | export const auth0 = createAuth0( 15 | { 16 | domain, 17 | clientId, 18 | authorizationParams: { 19 | audience, 20 | redirect_uri: window.location.origin 21 | }, 22 | useFormData: res?.useFormData || true 23 | }, 24 | { errorPath: '/error' } 25 | ); 26 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rootDir: './', 3 | clearMocks: true, 4 | testEnvironment: 'jsdom', 5 | moduleFileExtensions: ['ts', 'js'], 6 | testMatch: ['**/__tests__/**/*.test.ts'], 7 | coveragePathIgnorePatterns: [ 8 | '/node_modules/', 9 | './cypress', 10 | './jest.config.js', 11 | './__tests__', 12 | './src/global.ts' 13 | ], 14 | reporters: [ 15 | 'default', 16 | ['jest-junit', { outputDirectory: 'test-results/jest' }] 17 | ], 18 | coverageReporters: ['lcov', 'text', 'text-summary'], 19 | preset: 'ts-jest/presets/default-legacy', 20 | setupFiles: ['/jest.setup.js'], 21 | transform: { 22 | '.*\\.test\\.ts': [ 23 | 'ts-jest', 24 | { 25 | tsconfig: 'tsconfig.test.json' 26 | } 27 | ] 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /playground/components/Profile.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 31 | 32 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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.config.js", 21 | "cypress-version": "13", 22 | "project_name": "Auth0 Vue SDK", 23 | "exclude": [], 24 | "parallels": "5", 25 | "npm_dependencies": { 26 | "qss": "2.0.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 | -------------------------------------------------------------------------------- /playground/index.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | // Fix for https://github.com/ezolenko/rollup-plugin-typescript2/issues/129#issuecomment-454558185 3 | // @ts-ignore 4 | import Playground from './App.vue'; 5 | import { createRouter } from './router'; 6 | import { auth0 } from './auth0'; 7 | 8 | const defaultDomain = 'http://127.0.0.1:3000'; 9 | const defaultClientId = 'testing'; 10 | const defaultAudience = 'Test'; 11 | 12 | const res = localStorage.getItem('vue-playground-data') 13 | ? JSON.parse(localStorage.getItem('vue-playground-data') as string) 14 | : undefined; 15 | const domain = res?.domain || defaultDomain; 16 | const client_id = res?.client_id || defaultClientId; 17 | const audience = res?.audience || defaultAudience; 18 | 19 | createApp(Playground) 20 | .use( 21 | createRouter({ 22 | domain, 23 | client_id, 24 | audience 25 | }) 26 | ) 27 | .use(auth0) 28 | .mount('#app'); 29 | -------------------------------------------------------------------------------- /.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/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 | 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 | 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@v5 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/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 considered by StaleBot. Defaults to `[]` (disabled) 10 | onlyLabels: 11 | - 'more info needed' 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/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 | -------------------------------------------------------------------------------- /playground/router.ts: -------------------------------------------------------------------------------- 1 | import { createRouter as createVueRouter, createWebHistory } from 'vue-router'; 2 | import { authGuard } from '../src/guard'; 3 | // Fix for https://github.com/ezolenko/rollup-plugin-typescript2/issues/129#issuecomment-454558185 4 | // @ts-ignore 5 | import Home from './components/Home.vue'; 6 | // Fix for https://github.com/ezolenko/rollup-plugin-typescript2/issues/129#issuecomment-454558185 7 | // @ts-ignore 8 | import Profile from './components/Profile.vue'; 9 | // @ts-ignore 10 | import Error from './components/Error.vue'; 11 | 12 | export function createRouter({ client_id, domain, audience }: any) { 13 | return createVueRouter({ 14 | linkActiveClass: 'btn-primary', 15 | routes: [ 16 | { 17 | path: '/', 18 | name: 'home', 19 | component: Home, 20 | props: { 21 | client_id, 22 | domain, 23 | audience 24 | } 25 | }, 26 | { 27 | path: '/profile', 28 | name: 'profile', 29 | component: Profile, 30 | beforeEnter: authGuard 31 | }, 32 | { 33 | path: '/error', 34 | name: 'error', 35 | component: Error 36 | } 37 | ], 38 | history: createWebHistory() 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /src/interfaces/auth0-plugin-options.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Additional Configuration for the Auth0 Vue plugin 3 | */ 4 | export interface Auth0PluginOptions { 5 | /** 6 | * By default, if the page URL has code and state parameters, the SDK will assume it should handle it and attempt to exchange the code for a token. 7 | * 8 | * In situations where you are combining our SDK with other libraries that use the same `code` and `state` parameters, 9 | * you will need to ensure our SDK can differentiate between requests it should and should not handle. 10 | * 11 | * In these cases you can instruct the client to ignore certain URLs by setting `skipRedirectCallback`. 12 | * 13 | * ```js 14 | * createAuth0({}, { 15 | * skipRedirectCallback: window.location.pathname === '/other-callback' 16 | * }) 17 | * ``` 18 | * 19 | * **Note**: In the above example, `/other-callback` is an existing route, with a `code` (or `error` in case when something went wrong) and `state`, that will be handled 20 | * by any other SDK. 21 | * 22 | */ 23 | skipRedirectCallback?: boolean; 24 | 25 | /** 26 | * Path in your application to redirect to when the Authorization server 27 | * returns an error. Defaults to `/` 28 | */ 29 | errorPath?: string; 30 | } 31 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { inject } from 'vue'; 2 | import './global'; 3 | import { Auth0Plugin } from './global'; 4 | import type { 5 | Auth0VueClient, 6 | Auth0PluginOptions, 7 | Auth0VueClientOptions 8 | } from './global'; 9 | import { AUTH0_INJECTION_KEY, AUTH0_TOKEN } from './token'; 10 | import { deprecateRedirectUri } from './utils'; 11 | 12 | export * from './global'; 13 | export { AUTH0_INJECTION_KEY } from './token'; 14 | 15 | declare module 'vue' { 16 | export interface ComponentCustomProperties { 17 | [AUTH0_TOKEN]: Auth0VueClient; 18 | } 19 | } 20 | 21 | /** 22 | * Creates the Auth0 plugin. 23 | * 24 | * @param clientOptions The Auth Vue Client Options 25 | * @param pluginOptions Additional Plugin Configuration Options 26 | * @returns An instance of Auth0Plugin 27 | */ 28 | export function createAuth0( 29 | clientOptions: Auth0VueClientOptions, 30 | pluginOptions?: Auth0PluginOptions 31 | ) { 32 | deprecateRedirectUri(clientOptions); 33 | return new Auth0Plugin(clientOptions, pluginOptions); 34 | } 35 | 36 | /** 37 | * Returns the registered Auth0 instance using Vue's `inject`. 38 | * @returns An instance of Auth0VueClient 39 | */ 40 | export function useAuth0(): Auth0VueClient { 41 | return inject(AUTH0_INJECTION_KEY) as Auth0VueClient; 42 | } 43 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Jest (current file)", 11 | "program": "${workspaceFolder}/node_modules/.bin/jest", 12 | "args": ["${fileBasenameNoExtension}"], 13 | "console": "integratedTerminal", 14 | "internalConsoleOptions": "neverOpen", 15 | "disableOptimisticBPs": true, 16 | "skipFiles": ["/**"] 17 | }, 18 | { 19 | "type": "node", 20 | "request": "launch", 21 | "name": "Jest (all tests)", 22 | "program": "${workspaceFolder}/node_modules/.bin/jest", 23 | "args": ["--runInBand"], 24 | "console": "integratedTerminal", 25 | "internalConsoleOptions": "neverOpen", 26 | "disableOptimisticBPs": true, 27 | "skipFiles": ["/**"] 28 | }, 29 | { 30 | "type": "node", 31 | "request": "launch", 32 | "name": "Build", 33 | "program": "${workspaceFolder}/node_modules/.bin/rollup", 34 | "args": ["-m", "-c"], 35 | "console": "integratedTerminal" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /.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/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 }} -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution 2 | 3 | Please read [Auth0's contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md). 4 | 5 | ## Environment setup 6 | 7 | - Make sure you have node and npm installed 8 | - Run `npm install` to install dependencies 9 | - Follow the local development steps below to get started 10 | 11 | ## Local development 12 | 13 | - `npm install`: install dependencies 14 | - `npm start`: starts development http server at [http://localhost:3000](http://localhost:3000) with live reload enabled 15 | - `npm run test`: run unit tests 16 | - `npm run test:watch`: run unit tests continuously 17 | - `npm run test:integration`: run integration tests 18 | - `npm run test:watch:integration`: run integration tests continuously 19 | - `npm run build`: build distribution files 20 | - `npm run test:es-check`: check if distribution files are compatible with browsers 21 | - `npm run print-bundle-size`: print the final bundle size of distribution files 22 | 23 | ## Testing 24 | 25 | ### Adding tests 26 | 27 | - Unit tests go inside [\_\_tests\_\_](https://github.com/auth0/auth0-vue/tree/master/__tests__) 28 | - Integration tests go inside [cypress/integration](https://github.com/auth0/auth0-vue/tree/master/cypress/integration) 29 | 30 | ### Running tests 31 | 32 | Run unit and integration tests before opening a PR: 33 | 34 | ```bash 35 | npm run test 36 | npm run test:integration 37 | ``` 38 | 39 | Also include any information about essential manual tests. 40 | -------------------------------------------------------------------------------- /.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 | schedule: 13 | - cron: '37 10 * * 2' 14 | 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 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 | analyze: 26 | name: Check for Vulnerabilities 27 | runs-on: ubuntu-latest 28 | 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | language: [javascript] 33 | 34 | steps: 35 | - if: github.actor == 'dependabot[bot]' || github.event_name == 'merge_group' 36 | 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. 37 | 38 | - name: Checkout 39 | uses: actions/checkout@v5 40 | 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v3 43 | with: 44 | languages: ${{ matrix.language }} 45 | queries: +security-and-quality 46 | 47 | - name: Autobuild 48 | uses: github/codeql-action/autobuild@v3 49 | 50 | - name: Perform CodeQL Analysis 51 | uses: github/codeql-action/analyze@v3 52 | with: 53 | category: '/language:${{ matrix.language }}' 54 | -------------------------------------------------------------------------------- /.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 | 9 | permissions: 10 | contents: write 11 | id-token: write # For publishing to npm using --provenance 12 | 13 | ### 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. 14 | ### 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. 15 | ### TODO: Also remove `npm-release` workflow from this repo's .github/workflows folder once the repo is public. 16 | 17 | jobs: 18 | rl-scanner: 19 | uses: ./.github/workflows/rl-secure.yml 20 | with: 21 | node-version: 22 ## depends if build requires node else we can remove this. 22 | artifact-name: 'auth0-vue.tgz' ## Will change respective to Repository 23 | secrets: 24 | RLSECURE_LICENSE: ${{ secrets.RLSECURE_LICENSE }} 25 | RLSECURE_SITE_KEY: ${{ secrets.RLSECURE_SITE_KEY }} 26 | SIGNAL_HANDLER_TOKEN: ${{ secrets.SIGNAL_HANDLER_TOKEN }} 27 | PRODSEC_TOOLS_USER: ${{ secrets.PRODSEC_TOOLS_USER }} 28 | PRODSEC_TOOLS_TOKEN: ${{ secrets.PRODSEC_TOOLS_TOKEN }} 29 | PRODSEC_TOOLS_ARN: ${{ secrets.PRODSEC_TOOLS_ARN }} 30 | 31 | release: 32 | uses: ./.github/workflows/npm-release.yml 33 | needs: rl-scanner ## this is important as this will not let release job to run until rl-scanner is done 34 | with: 35 | node-version: 22 36 | require-build: true 37 | secrets: 38 | npm-token: ${{ secrets.NPM_TOKEN }} 39 | github-token: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Changes 4 | 5 | 11 | 12 | ### References 13 | 14 | 24 | 25 | ### Testing 26 | 27 | 30 | 31 | - [ ] This change adds unit test coverage 32 | - [ ] This change adds integration test coverage 33 | - [ ] This change has been tested on the latest version of the platform/language 34 | 35 | ### Checklist 36 | 37 | - [ ] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md) 38 | - [ ] I have read the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md) 39 | - [ ] All code quality tools/guidelines have been run/followed 40 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /* eslint-disable @typescript-eslint/ban-types */ 3 | import { watchEffect } from 'vue'; 4 | 5 | /** 6 | * @ignore 7 | * Run watchEffect untill the watcher returns true, then stop the watch. 8 | * Once it returns true, the promise will resolve. 9 | */ 10 | export function watchEffectOnceAsync(watcher: () => T) { 11 | return new Promise(resolve => { 12 | watchEffectOnce(watcher, resolve); 13 | }); 14 | } 15 | 16 | /** 17 | * @ignore 18 | * Run watchEffect untill the watcher returns true, then stop the watch. 19 | * Once it returns true, it will call the provided function. 20 | */ 21 | export function watchEffectOnce(watcher: () => T, fn: Function) { 22 | const stopWatch = watchEffect(() => { 23 | if (watcher()) { 24 | fn(); 25 | stopWatch(); 26 | } 27 | }); 28 | } 29 | 30 | /** 31 | * @ignore 32 | * Helper function to bind methods to itself, useful when using Vue's `provide` / `inject` API's. 33 | */ 34 | export function bindPluginMethods(plugin: any, exclude: string[]) { 35 | Object.getOwnPropertyNames(Object.getPrototypeOf(plugin)) 36 | .filter(method => !exclude.includes(method)) 37 | // eslint-disable-next-line security/detect-object-injection 38 | .forEach(method => (plugin[method] = plugin[method].bind(plugin))); 39 | } 40 | 41 | /** 42 | * @ignore 43 | * Helper function to map the v1 `redirect_uri` option to the v2 `authorizationParams.redirect_uri` 44 | * and log a warning. 45 | */ 46 | export function deprecateRedirectUri(options?: any) { 47 | if (options?.redirect_uri) { 48 | console.warn( 49 | 'Using `redirect_uri` has been deprecated, please use `authorizationParams.redirect_uri` instead as `redirectUri` will be no longer supported in a future version' 50 | ); 51 | options.authorizationParams = options.authorizationParams || {}; 52 | options.authorizationParams.redirect_uri = options.redirect_uri; 53 | delete options.redirect_uri; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /docs/assets/search.js: -------------------------------------------------------------------------------- 1 | window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE7WaXW/bNhSG/wtzq6Y+h/6+K7Juy1a0wdp1GITAUCXG5qJIgkSlC4z894GUJZEi5dJxdtXC4vvyHPIhdURmT8r8e0XW4Z7c8ywh6/k0IFn0wMiaxCWLBHtXi92EBKQuU7Imd3UWC55n1Vvt6eVOPKQkIHEaVRWryJqQ58BhWFejbu2jo1YUO6t3f375dbK5/vjb+6sv158+bn5//3fn+hiVPPqWsuqto9XRDnA273sois8iEqyz5Zlg5V0US9/Ds6NmMME+dRGVWyZ+6HXRtdMsA1JEJcuEHtRIzHIIb9J6y7NPhRpYZ49WK/88qnte/MESXrJYXEVp+i2K70/o42JEP5Ktnc5IWKws8/ImErtTYtFFLwjAGvivNbtKuVSOBdG18B9wXn3Io4RnW0/TC11wJKs+2NGOZTuWCR5HgiX+3Q9l5wRRV6z07fnQ9qycky/5Pcuu0og/jC+dYcYD0TkBKCR9O24bn9ghTqbLrsM03/LsLy52N3lRF749W6pXCaHdF06OQhOeE8guypKUee9ug2hG1eeEFO9YfP+ZVRXPM99ABppzut8y8S6OWVUpwj/zlGUiffINZFT9eiGdTO64/EyE8/oUbpvWZ47DT0VefMyz2F2iuJPXNed0X72g++r1ut+yjJWRYNLvpszzO/8hsIVnLVBVBf/MRLzzf1MNRacGMFp4/KjoGzb0LoY/KGSP2BsNvG3b3fJDfrxidbXz/ejov1N+qaMyOfolo1ocN57NaB//ZiOeCuZpeTH8sVO753/YfJjI/xLUGziE9QZfI7B+GqIjExB5Df0Qe6X4AfF6G/+aOypG3yeW4UXTeHwJG2GOdFh6rgN3BCNq35BuA8KzhP1L1nvyyEpVNqwJXtLLFQnIHWdpIg8ImlgDEucPD803TpLHtfrv7aHZVxaLvJSNm9ZvJyQIJwGFy8USbm+DsBWrB+qH1qP/RQmBBCG4hGAJwRAiCUJ0CdESoiGkJAipS0gtITWEUxKEU5dwagmnhnBGgnDmEs4s4cwQzkkQzl3CuSWcG8IFCcKFS7iwhAtDuCRBuHQJl5ZwaQhXJAhXLuHKEq5MACQP4GQHbHhgQI/Cx82PAyCTIJBcgJMhsCECkyKQbICTI7BBApMkkHyAkyWwYQKTJpCMgJMnsIECkyiQnICTKbChApMqkKyAkyuwwQKTLJC8gJMtsOECky6QzICTL7ABA5MwlMygkzC0CUOTMJTMoJMwtAnDwR6lNin3LuXYpkzCUDKDTsLQJgxNwlAyg07C0CYMTcJQMoNOwtAmDE3CUDKDTsLQJgxNwlAyg4sA55fzBZpimzA0CUPJDDoJQ5swNAlDyQyunD3bhKFJGJ2MiqlNGDUJo5IZ6sST2oRRkzAqmaFOPKlNGB28CdWr0IkndbwMTcLodHSqqE3Y4SdVgTyyUrDkuqlEwrArY/dkcyhPcNUWTHuCK7LePweETuS/z31Zon7tKhP5TPaoirTeqb9I2BNKfR2q5jpAs9Fc/EzkR9iGZ/8wVflu7tmTkaCWn79foc6n87bu6+1mvd3M3+6xZvHhFLu3WvZWyxdYOYJDLTr0D2/bfEJoczDRJgFO83GERUGz85sEdd5WtedtvRUseitY+FmpL6mouSPrnbQU/XDvfazxQm0q0W8uG7e79rhC85pqXlMvr8OZtTZK2iICv1WkPAp1b9P7aIPtN9btSVBS5EXRnARpmWlRoV9UWyYidbIo5Mli1Z12aqlqQw9+Q2+afudiVzTHlZqrtivCytdVZp01B3Fa1hr86LeWmlPv9gs07k69tfjmWnxzL1OeqHTjw42K5qXtkOC3OnkVmfdRmpu2rsBvYfEqbS/Xeh9tAvzGX11cuCdTW1Hgt6I6s7K7BdH8tF0W/HbZ9nRaw0IbJ/Qbp8bEte1rNKAfDW1eKlGXpbb0cXEoC/zGrhpbCPqb2I8zebF9bBloeful3f4lQG+hseGXXl3Z7xJtifut8OZaVTPQHTwsbgNS8IKlPGNkHd4+P/8HunTlsXMiAAA="; -------------------------------------------------------------------------------- /.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: 22 23 | CACHE_KEY: '${{ github.event.pull_request.head.sha || github.ref }}-${{ github.run_id }}-${{ github.run_attempt }}' 24 | 25 | jobs: 26 | build: 27 | name: Build Package 28 | runs-on: ubuntu-latest 29 | 30 | steps: 31 | - name: Checkout code 32 | uses: actions/checkout@v5 33 | with: 34 | ref: ${{ github.event.pull_request.head.sha || github.ref }} 35 | 36 | - name: Build package 37 | uses: ./.github/actions/build 38 | with: 39 | node: ${{ env.NODE_VERSION }} 40 | 41 | - name: Save build artifacts 42 | uses: actions/cache/save@v4 43 | with: 44 | path: . 45 | key: ${{ env.CACHE_KEY }} 46 | 47 | browserstack: 48 | needs: build # Only run if unit tests pass 49 | 50 | name: BrowserStack Tests 51 | runs-on: ubuntu-latest 52 | 53 | steps: 54 | - name: Checkout code 55 | uses: actions/checkout@v5 56 | with: 57 | ref: ${{ github.event.pull_request.head.sha || github.ref }} 58 | 59 | - name: Build package 60 | uses: ./.github/actions/build 61 | with: 62 | node: ${{ env.NODE_VERSION }} 63 | 64 | - name: Run tests 65 | shell: bash 66 | run: npx concurrently --raw --kill-others --success first "npm:dev" "wait-on http://127.0.0.1:3000/ && browserstack-cypress run --sync --no-wrap --build-name ${{ github.ref }}-${{ github.sha }}" 67 | env: 68 | BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} 69 | BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} 70 | -------------------------------------------------------------------------------- /.github/workflows/rl-secure.yml: -------------------------------------------------------------------------------- 1 | name: RL-Secure Workflow 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | node-version: ## depends if build requires node else we can remove this. 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@v5 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 -------------------------------------------------------------------------------- /.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-vue#readme), [Examples](https://github.com/auth0/auth0-vue/blob/main/EXAMPLES.md), and [FAQ](https://github.com/auth0/auth0-vue/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-vue/) and have not found a suitable solution or answer. 14 | required: true 15 | - label: I have searched the [issues](https://github.com/auth0/auth0-vue/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/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 | -------------------------------------------------------------------------------- /src/guard.ts: -------------------------------------------------------------------------------- 1 | import type { RouteLocation } from 'vue-router'; 2 | import { watchEffectOnceAsync } from './utils'; 3 | import { client as auth0Client } from './plugin'; 4 | import { AUTH0_TOKEN } from './token'; 5 | import type { Auth0VueClient } from './interfaces'; 6 | import type { App } from 'vue'; 7 | import { unref } from 'vue'; 8 | import type { RedirectLoginOptions } from '@auth0/auth0-spa-js'; 9 | 10 | async function createGuardHandler( 11 | client: Auth0VueClient, 12 | to: RouteLocation, 13 | redirectLoginOptions?: RedirectLoginOptions 14 | ) { 15 | const fn = async () => { 16 | if (unref(client.isAuthenticated)) { 17 | return true; 18 | } 19 | 20 | await client.loginWithRedirect({ 21 | appState: { target: to.fullPath }, 22 | ...redirectLoginOptions 23 | }); 24 | 25 | return false; 26 | }; 27 | 28 | if (!unref(client.isLoading)) { 29 | return fn(); 30 | } 31 | 32 | await watchEffectOnceAsync(() => !unref(client.isLoading)); 33 | 34 | return fn(); 35 | } 36 | 37 | /** 38 | * The options used when creating an AuthGuard. 39 | */ 40 | export interface AuthGuardOptions { 41 | /** 42 | * The vue application 43 | */ 44 | app?: App; 45 | 46 | /** 47 | * Route specific options to use when being redirected to Auth0 48 | */ 49 | redirectLoginOptions?: RedirectLoginOptions; 50 | } 51 | 52 | /** 53 | * 54 | * @param [app] The vue application 55 | */ 56 | export function createAuthGuard( 57 | app?: App 58 | ): (to: RouteLocation) => Promise; 59 | 60 | /** 61 | * 62 | * @param [options] The options used when creating an AuthGuard. 63 | */ 64 | export function createAuthGuard( 65 | options?: AuthGuardOptions 66 | ): (to: RouteLocation) => Promise; 67 | 68 | export function createAuthGuard( 69 | appOrOptions?: App | AuthGuardOptions 70 | ): (to: RouteLocation) => Promise { 71 | const { app, redirectLoginOptions } = 72 | !appOrOptions || 'config' in appOrOptions 73 | ? { app: appOrOptions as App, redirectLoginOptions: undefined } 74 | : (appOrOptions as AuthGuardOptions); 75 | 76 | return async (to: RouteLocation) => { 77 | // eslint-disable-next-line security/detect-object-injection 78 | const auth0 = app 79 | ? (app.config.globalProperties[AUTH0_TOKEN] as Auth0VueClient) 80 | : (unref(auth0Client) as Auth0VueClient); 81 | 82 | return createGuardHandler(auth0, to, redirectLoginOptions); 83 | }; 84 | } 85 | 86 | export async function authGuard(to: RouteLocation) { 87 | const auth0 = unref(auth0Client) as Auth0VueClient; 88 | 89 | return createGuardHandler(auth0, to); 90 | } 91 | -------------------------------------------------------------------------------- /.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@v5 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 }} 81 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | ## Environment 2 | 3 | - Node >= 12.x 4 | 5 | ## Building 6 | 7 | The SDK uses [Rollup](https://rollupjs.org/guide/en/) to compile all JavaScript assets into a set of output modules to be consumed by other module builders such as [Webpack](https://webpack.js.org/) and Rollup, or directly into and HTML file via the CDN. 8 | 9 | To perform a build, use the `build` script: 10 | 11 | ``` 12 | npm run build 13 | ``` 14 | 15 | ### Bundle stats 16 | 17 | Bundle size statistics can be generated when `WITH_STATS=true` is present in the environment. This outputs production bundle stats into the terminal when running `npm run build`, but also generates a visualization into the `bundle-stats` folder. 18 | 19 | To build with stats then view the results, do: 20 | 21 | ``` 22 | WITH_STATS=true npm run build 23 | npm run serve:stats 24 | ``` 25 | 26 | Then browse to http://localhost:5000 to view an HTML-based bundle size report. 27 | 28 | ## Running Tests 29 | 30 | ### Unit tests 31 | 32 | Unit tests can be executed using [Jest](https://jestjs.io/) by issuing the following command: 33 | 34 | ``` 35 | npm test 36 | ``` 37 | 38 | To interactively perform tests using Jest's `watch` mode, use: 39 | 40 | ``` 41 | npm run test:watch 42 | ``` 43 | 44 | ### Integration tests 45 | 46 | Integration tests can be run through [Cypress](https://www.cypress.io/) to perform integration testing using the SDK and Auth0. 47 | 48 | To run these, use: 49 | 50 | ``` 51 | npm run test:integration 52 | ``` 53 | 54 | To perform these tests interactively and watch the output, use: 55 | 56 | ``` 57 | npm run test:watch:integration 58 | ``` 59 | 60 | ### Test coverage 61 | 62 | Coverage is automatically generated just by running `npm test`. To view the coverage output, use: 63 | 64 | ``` 65 | npm run serve:coverage 66 | ``` 67 | 68 | Then, browse to http://localhost:5000 to view an HTML-based coverage report. 69 | 70 | ## The SDK Playground 71 | 72 | The SDK provides a simple [Vue JS](https://vuejs.org/) app to test out and experiment with features of the SDK. This Playground is also used by the integration tests to verify behaviors. If you make changes to the Playground that are to be committed, ensure that the integration tests pass. 73 | 74 | To test the SDK manually and play around with the various options and features, you can use the Playground by cloning this repository and using: 75 | 76 | ``` 77 | # Install dependencies 78 | npm i 79 | 80 | # Run the playground app 81 | npm start 82 | ``` 83 | 84 | This will open a web server on `http://localhost:3000` and display a simple web app that allows you to manually perform various features of the SDK. This is preconfigured with an Auth0 tenant and client ID but you may change this to your own for testing. 85 | 86 | You may specify a different port for the development server by specifying the `DEV_PORT` environment variable: 87 | 88 | ``` 89 | DEV_PORT=8080 npm start 90 | ``` 91 | 92 | The Playground may not cover all use cases. In this case, modify the [index.html file](https://github.com/auth0/auth0-vue/blob/master/static/index.html) to configure the SDK as desired to invoke different behaviors. 93 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 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: 22 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@v5 32 | 33 | - name: Build package 34 | uses: ./.github/actions/build 35 | with: 36 | node: ${{ env.NODE_VERSION }} 37 | 38 | - name: Save build artifacts 39 | uses: actions/cache/save@v4 40 | with: 41 | path: . 42 | key: ${{ env.CACHE_KEY }} 43 | 44 | unit: 45 | needs: build # Require build to complete before running tests 46 | 47 | name: Unit Tests 48 | runs-on: ubuntu-latest 49 | 50 | steps: 51 | - name: Checkout code 52 | uses: actions/checkout@v5 53 | 54 | - name: Setup Node 55 | uses: actions/setup-node@v5 56 | with: 57 | node-version: ${{ env.NODE_VERSION }} 58 | cache: npm 59 | 60 | - name: Restore build artifacts 61 | uses: actions/cache/restore@v4 62 | with: 63 | path: . 64 | key: ${{ env.CACHE_KEY }} 65 | 66 | - name: Run tests 67 | run: npm run test -- --maxWorkers=2 68 | 69 | - name: Upload coverage 70 | uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # pin@5.5.0 71 | 72 | compatibility: 73 | needs: build # Require build to complete before running tests 74 | 75 | name: Compatibility Tests 76 | runs-on: ubuntu-latest 77 | 78 | steps: 79 | - name: Checkout code 80 | uses: actions/checkout@v5 81 | 82 | - name: Setup Node 83 | uses: actions/setup-node@v5 84 | with: 85 | node-version: ${{ env.NODE_VERSION }} 86 | cache: npm 87 | 88 | - name: Restore build artifacts 89 | uses: actions/cache/restore@v4 90 | with: 91 | path: . 92 | key: ${{ env.CACHE_KEY }} 93 | 94 | - name: Run tests 95 | run: npm run test:es-check 96 | 97 | lint: 98 | needs: build # Require build to complete before running tests 99 | 100 | name: Lint Tests 101 | runs-on: ubuntu-latest 102 | 103 | steps: 104 | - name: Checkout code 105 | uses: actions/checkout@v5 106 | 107 | - name: Setup Node 108 | uses: actions/setup-node@v5 109 | with: 110 | node-version: ${{ env.NODE_VERSION }} 111 | cache: npm 112 | 113 | - name: Restore build artifacts 114 | uses: actions/cache/restore@v4 115 | with: 116 | path: . 117 | key: ${{ env.CACHE_KEY }} 118 | 119 | - name: Run tests 120 | run: npm run lint 121 | -------------------------------------------------------------------------------- /scripts/oidc-provider.js: -------------------------------------------------------------------------------- 1 | import { Provider, interactionPolicy } from 'oidc-provider'; 2 | 3 | const { base, Prompt, Check } = interactionPolicy; 4 | const policy = base(); 5 | 6 | policy.add( 7 | new Prompt( 8 | { name: 'noop', requestable: false }, 9 | new Check('foo', 'bar', ctx => { 10 | if (ctx.query?.scope?.includes('offline_access')) { 11 | ctx.oidc.params.scope = `${ctx.oidc.params.scope} offline_access`; 12 | } 13 | return Check.NO_NEED_TO_PROMPT; 14 | }) 15 | ), 16 | 0 17 | ); 18 | 19 | const config = { 20 | clients: [ 21 | { 22 | client_id: 'testing', 23 | redirect_uris: ['http://127.0.0.1:3000', 'http://localhost:3000'], 24 | token_endpoint_auth_method: 'none', 25 | grant_types: ['authorization_code', 'refresh_token'] 26 | }, 27 | { 28 | client_id: 'multi-client-1', 29 | redirect_uris: [ 30 | 'http://127.0.0.1:3000/multiple_clients.html', 31 | 'http://localhost:3000/multiple_clients.html' 32 | ], 33 | token_endpoint_auth_method: 'none', 34 | grant_types: ['authorization_code', 'refresh_token'] 35 | }, 36 | { 37 | client_id: 'multi-client-2', 38 | redirect_uris: [ 39 | 'http://127.0.0.1:3000/multiple_clients.html', 40 | 'http://localhost:3000/multiple_clients.html' 41 | ], 42 | token_endpoint_auth_method: 'none', 43 | grant_types: ['authorization_code', 'refresh_token'] 44 | }, 45 | { 46 | client_id: 'multi-client-3', 47 | redirect_uris: [ 48 | 'http://127.0.0.1:3000/multiple_clients.html', 49 | 'http://localhost:3000/multiple_clients.html' 50 | ], 51 | token_endpoint_auth_method: 'none', 52 | grant_types: ['authorization_code', 'refresh_token'] 53 | } 54 | ], 55 | claims: { 56 | profile: ['name'] 57 | }, 58 | routes: { 59 | authorization: '/authorize', // lgtm [js/hardcoded-credentials] 60 | token: '/oauth/token', 61 | end_session: '/v2/logout' 62 | }, 63 | scopes: ['openid', 'offline_access', 'profile'], 64 | clientBasedCORS(ctx, origin, client) { 65 | return true; 66 | }, 67 | features: { 68 | webMessageResponseMode: { 69 | enabled: true 70 | }, 71 | claimsParameter: { 72 | enabled: true 73 | } 74 | }, 75 | conformIdTokenClaims: false, 76 | rotateRefreshToken: true, 77 | interactions: { 78 | policy 79 | }, 80 | findAccount(ctx, id) { 81 | return { 82 | accountId: id, 83 | claims(use, scope, claims) { 84 | return { 85 | sub: id, 86 | name: id, 87 | ...(claims?.org_id ? { org_id: claims.org_id.values[0] } : null) 88 | }; 89 | } 90 | }; 91 | } 92 | }; 93 | 94 | export function createApp(opts) { 95 | const issuer = `http://127.0.0.1:${opts.port || 3000}/`; 96 | const provider = new Provider(issuer, config); 97 | 98 | provider.use(async (ctx, next) => { 99 | await next(); 100 | 101 | if (ctx.oidc?.route === 'end_session_success') { 102 | ctx.redirect('http://127.0.0.1:3000'); 103 | } 104 | }); 105 | 106 | return provider.app; 107 | } 108 | -------------------------------------------------------------------------------- /.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-vue sample app](https://github.com/auth0-samples/auth0-vue-samples/tree/master/01-Login) (or N/A). 17 | required: true 18 | - label: I have looked into the [Readme](https://github.com/auth0/auth0-vue#readme), [Examples](https://github.com/auth0/auth0-vue/blob/main/EXAMPLES.md), and [FAQ](https://github.com/auth0/auth0-vue/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-vue/) and have not found a suitable solution or answer. 21 | required: true 22 | - label: I have searched the [issues](https://github.com/auth0/auth0-vue/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-vue version 61 | validations: 62 | required: true 63 | 64 | - type: input 65 | id: environment-vue-version 66 | attributes: 67 | label: Vue 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 | -------------------------------------------------------------------------------- /cypress/integration/test.js: -------------------------------------------------------------------------------- 1 | const EMAIL = Cypress.env('USER_EMAIL') || 'testing'; 2 | const PASSWORD = Cypress.env('USER_PASSWORD') || 'testing'; 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 login = () => { 11 | cy.get('.login-card') 12 | .should('have.length', 1) 13 | .then($form => { 14 | cy.get('input[name=login]').clear().type(EMAIL); 15 | cy.get('input[name=password]').clear().type(PASSWORD); 16 | cy.get('.login-submit').click(); 17 | cy.get('.login-submit').click(); 18 | }); 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 | const logout = () => { 34 | cy.get('button[data-cy=logout]').should('be.visible').click(); 35 | cy.get('button[name=logout]').should('be.visible').click(); 36 | }; 37 | 38 | describe('Smoke tests', () => { 39 | afterEach(fixCookies); 40 | 41 | it('shows default logged out', () => { 42 | cy.visit('/'); 43 | cy.get('[data-cy=authenticated]').contains('false'); 44 | }); 45 | 46 | it('shows logged in once logged in', () => { 47 | cy.visit('/'); 48 | cy.get('#login_redirect').should('be.visible').click(); 49 | 50 | login(); 51 | 52 | cy.get('[data-cy=authenticated]').contains('true'); 53 | 54 | logout(); 55 | }); 56 | 57 | it('do redirect login and show user and access token', () => { 58 | cy.visit('/'); 59 | cy.get('#login_redirect').should('be.visible').click(); 60 | 61 | cy.url().should('include', 'http://127.0.0.1:3000'); 62 | 63 | login(); 64 | 65 | cy.get('[data-cy=profile]').contains(`"sub": "${EMAIL}"`); 66 | cy.get('[data-cy=access-token]').should('not.exist'); 67 | cy.get('[data-cy=get-token]').click(); 68 | cy.get('[data-cy=access-token]').should('have.length', 1); 69 | 70 | logout(); 71 | 72 | cy.get('#login_redirect').should('be.visible'); 73 | }); 74 | 75 | it('redirect to login when accessing a protected route', () => { 76 | cy.visit('/'); 77 | cy.get('[data-cy=profile-menu]').should('be.visible').click(); 78 | 79 | cy.url().should('not.contain', 'http://127.0.0.1:3000/profile'); 80 | cy.url().should('include', 'http://127.0.0.1:3000'); 81 | 82 | login(); 83 | 84 | cy.url().should('include', 'http://127.0.0.1:3000/profile'); 85 | cy.get('.profile-header').contains(EMAIL); 86 | 87 | cy.get('[data-cy=home-menu]').should('be.visible').click(); 88 | 89 | logout(); 90 | }); 91 | 92 | it('does not redirect to login when accessing a protected route while authenticated', () => { 93 | cy.visit('/'); 94 | cy.get('#login_redirect').should('be.visible').click(); 95 | 96 | cy.url().should('include', 'http://127.0.0.1:3000'); 97 | 98 | login(); 99 | 100 | cy.get('[data-cy=profile-menu]').should('be.visible').click(); 101 | 102 | cy.url().should('include', 'http://127.0.0.1:3000/profile'); 103 | cy.get('.profile-header').contains(EMAIL); 104 | 105 | cy.get('[data-cy=home-menu]').should('be.visible').click(); 106 | 107 | logout(); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /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: #AF00DB; 9 | --dark-hl-3: #C586C0; 10 | --light-hl-4: #001080; 11 | --dark-hl-4: #9CDCFE; 12 | --light-hl-5: #0000FF; 13 | --dark-hl-5: #569CD6; 14 | --light-hl-6: #0070C1; 15 | --dark-hl-6: #4FC1FF; 16 | --light-hl-7: #800000; 17 | --dark-hl-7: #808080; 18 | --light-hl-8: #800000; 19 | --dark-hl-8: #569CD6; 20 | --light-hl-9: #000000FF; 21 | --dark-hl-9: #D4D4D4; 22 | --light-hl-10: #E50000; 23 | --dark-hl-10: #9CDCFE; 24 | --light-hl-11: #0000FF; 25 | --dark-hl-11: #CE9178; 26 | --light-hl-12: #008000; 27 | --dark-hl-12: #6A9955; 28 | --light-hl-13: #098658; 29 | --dark-hl-13: #B5CEA8; 30 | --light-hl-14: #267F99; 31 | --dark-hl-14: #4EC9B0; 32 | --light-code-background: #FFFFFF; 33 | --dark-code-background: #1E1E1E; 34 | } 35 | 36 | @media (prefers-color-scheme: light) { :root { 37 | --hl-0: var(--light-hl-0); 38 | --hl-1: var(--light-hl-1); 39 | --hl-2: var(--light-hl-2); 40 | --hl-3: var(--light-hl-3); 41 | --hl-4: var(--light-hl-4); 42 | --hl-5: var(--light-hl-5); 43 | --hl-6: var(--light-hl-6); 44 | --hl-7: var(--light-hl-7); 45 | --hl-8: var(--light-hl-8); 46 | --hl-9: var(--light-hl-9); 47 | --hl-10: var(--light-hl-10); 48 | --hl-11: var(--light-hl-11); 49 | --hl-12: var(--light-hl-12); 50 | --hl-13: var(--light-hl-13); 51 | --hl-14: var(--light-hl-14); 52 | --code-background: var(--light-code-background); 53 | } } 54 | 55 | @media (prefers-color-scheme: dark) { :root { 56 | --hl-0: var(--dark-hl-0); 57 | --hl-1: var(--dark-hl-1); 58 | --hl-2: var(--dark-hl-2); 59 | --hl-3: var(--dark-hl-3); 60 | --hl-4: var(--dark-hl-4); 61 | --hl-5: var(--dark-hl-5); 62 | --hl-6: var(--dark-hl-6); 63 | --hl-7: var(--dark-hl-7); 64 | --hl-8: var(--dark-hl-8); 65 | --hl-9: var(--dark-hl-9); 66 | --hl-10: var(--dark-hl-10); 67 | --hl-11: var(--dark-hl-11); 68 | --hl-12: var(--dark-hl-12); 69 | --hl-13: var(--dark-hl-13); 70 | --hl-14: var(--dark-hl-14); 71 | --code-background: var(--dark-code-background); 72 | } } 73 | 74 | :root[data-theme='light'] { 75 | --hl-0: var(--light-hl-0); 76 | --hl-1: var(--light-hl-1); 77 | --hl-2: var(--light-hl-2); 78 | --hl-3: var(--light-hl-3); 79 | --hl-4: var(--light-hl-4); 80 | --hl-5: var(--light-hl-5); 81 | --hl-6: var(--light-hl-6); 82 | --hl-7: var(--light-hl-7); 83 | --hl-8: var(--light-hl-8); 84 | --hl-9: var(--light-hl-9); 85 | --hl-10: var(--light-hl-10); 86 | --hl-11: var(--light-hl-11); 87 | --hl-12: var(--light-hl-12); 88 | --hl-13: var(--light-hl-13); 89 | --hl-14: var(--light-hl-14); 90 | --code-background: var(--light-code-background); 91 | } 92 | 93 | :root[data-theme='dark'] { 94 | --hl-0: var(--dark-hl-0); 95 | --hl-1: var(--dark-hl-1); 96 | --hl-2: var(--dark-hl-2); 97 | --hl-3: var(--dark-hl-3); 98 | --hl-4: var(--dark-hl-4); 99 | --hl-5: var(--dark-hl-5); 100 | --hl-6: var(--dark-hl-6); 101 | --hl-7: var(--dark-hl-7); 102 | --hl-8: var(--dark-hl-8); 103 | --hl-9: var(--dark-hl-9); 104 | --hl-10: var(--dark-hl-10); 105 | --hl-11: var(--dark-hl-11); 106 | --hl-12: var(--dark-hl-12); 107 | --hl-13: var(--dark-hl-13); 108 | --hl-14: var(--dark-hl-14); 109 | --code-background: var(--dark-code-background); 110 | } 111 | 112 | .hl-0 { color: var(--hl-0); } 113 | .hl-1 { color: var(--hl-1); } 114 | .hl-2 { color: var(--hl-2); } 115 | .hl-3 { color: var(--hl-3); } 116 | .hl-4 { color: var(--hl-4); } 117 | .hl-5 { color: var(--hl-5); } 118 | .hl-6 { color: var(--hl-6); } 119 | .hl-7 { color: var(--hl-7); } 120 | .hl-8 { color: var(--hl-8); } 121 | .hl-9 { color: var(--hl-9); } 122 | .hl-10 { color: var(--hl-10); } 123 | .hl-11 { color: var(--hl-11); } 124 | .hl-12 { color: var(--hl-12); } 125 | .hl-13 { color: var(--hl-13); } 126 | .hl-14 { color: var(--hl-14); } 127 | pre, code { background: var(--code-background); } 128 | -------------------------------------------------------------------------------- /tutorial/vue2-api.md: -------------------------------------------------------------------------------- 1 | # Call an API 2 | 3 | This tutorial will show the basics of how to get [Access Tokens](https://auth0.com/docs/secure/tokens/access-tokens) from [Auth0 SPA SDK](https://github.com/auth0/auth0-spa-js) to call APIs. 4 | 5 | **NOTE:** To get the most out of this tutorial, you should have an API ready to be called - what we show here is how to call the API and set the Access Token correctly. 6 | 7 | ## Create an Auth0 API 8 | 9 | For Auth0 to be able to issue Access Tokens for your API, you must create an API within your [Auth0 Dashboard](https://manage.auth0.com). 10 | 11 | In the [APIs section](https://manage.auth0.com/#/apis) of the Auth0 dashboard, click **Create API**. Provide a name and an identifier for your API. You will use the identifier later when you're configuring your Javascript Auth0 application instance. 12 | 13 | For **Signing Algorithm**, select **RS256**. 14 | 15 | ![Create API](create-api.png) 16 | 17 | ## Add the Audience 18 | 19 | To request Access Tokens in JWT format from Auth0, you must specify the `audience` value in the SDK configuration. This value should be the identifier that was given in the previous section above. 20 | 21 | Following on from the [previous tutorial](vue2-login.md), add your API Identifier value to the `audience` key in `auth_config.json` that we can then use when setting up the SDK: 22 | 23 | ```json 24 | { 25 | "domain": "", 26 | "clientId": "", 27 | "authorizationParams": { 28 | "audience": "" 29 | } 30 | } 31 | ``` 32 | 33 | Finally, open `main.js` and configure the Auth0 plugin with this authorizationParams value, which includes the audience: 34 | 35 | ```js 36 | // .. other imports 37 | 38 | // NEW - import the authorizationParams, which includes the audience 39 | import { domain, clientId, authorizationParams } from '../auth_config.json'; 40 | 41 | // .. 42 | 43 | Vue.use(Auth0Plugin, { 44 | domain, 45 | clientId, 46 | authorizationParams, // NEW - configure the plugin with the authorizationParams value 47 | onRedirectCallback: appState => { 48 | router.push( 49 | appState && appState.targetUrl 50 | ? appState.targetUrl 51 | : window.location.pathname 52 | ); 53 | } 54 | }); 55 | 56 | // .. more Vue configuration .. 57 | ``` 58 | 59 | ## Call the API Using an Access Token 60 | 61 | Modify the frontend Vue.js application to include a page that calls the API using an access token. Like the previous tutorial, you'll need to change the Vue router and add a new view with a button that calls the API. 62 | 63 | Here we are using [Axios](https://www.npmjs.com/package/axios) as an example, but you can use any HTTP library that supports sending custom or authentication headers. 64 | 65 | ### Install Axios 66 | 67 | Install the [`axios`](https://www.npmjs.com/package/axios) HTTP library, which will allow us to make HTTP calls out to the backend API: 68 | 69 | ```bash 70 | npm install axios 71 | ``` 72 | 73 | ### Add a new page 74 | 75 | Next, create a new file `ExternalApi.vue` inside the `views` folder, and add a button to call the API. 76 | 77 | Use the `getTokenSilently` method of the Auth0 wrapper to get an access token, and then use Axios to make the API call. Attach the access token to the call by setting it as the value for the `Authorization` header, as in the following example: 78 | 79 | ```html 80 | 85 | 86 | 106 | ``` 107 | 108 | ### Checkpoint 109 | 110 | Now you will be able to run the application, browse to the "External API" page and press the "Call" button. The application will make a call to your API endpoint. If the call authorizes correctly then no errors relating to authorization should be displayed in the console. 111 | 112 | > For a deep dive into making secure calls to an API from Vue, visit the [Complete Guide to Vue User Authentication with Auth0](https://auth0.com/blog/complete-guide-to-vue-user-authentication/#Calling-an-API). This guide provides you with additional details, such setting up a sample Express API server and getting test access tokens from the Auth0 Dashboard. 113 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import typescript from 'rollup-plugin-typescript2'; 4 | import { terser } from 'rollup-plugin-terser'; 5 | import sourcemaps from 'rollup-plugin-sourcemaps'; 6 | import livereload from 'rollup-plugin-livereload'; 7 | import visualizer from 'rollup-plugin-visualizer'; 8 | import webWorkerLoader from 'rollup-plugin-web-worker-loader'; 9 | import replace from '@rollup/plugin-replace'; 10 | import analyze from 'rollup-plugin-analyzer'; 11 | import dev from 'rollup-plugin-dev'; 12 | import vue from 'rollup-plugin-vue'; 13 | import { createApp } from './scripts/oidc-provider'; 14 | import pkg from './package.json'; 15 | 16 | const EXPORT_NAME = 'vueAuth0'; 17 | 18 | const isProduction = process.env.NODE_ENV === 'production'; 19 | const shouldGenerateStats = process.env.WITH_STATS === 'true'; 20 | const defaultDevPort = 3000; 21 | const serverPort = process.env.DEV_PORT || defaultDevPort; 22 | 23 | const visualizerOptions = { 24 | filename: 'bundle-stats/index.html' 25 | }; 26 | 27 | const getPlugins = shouldMinify => { 28 | return [ 29 | webWorkerLoader({ 30 | targetPlatform: 'browser', 31 | sourceMap: !isProduction, 32 | preserveSource: !isProduction, 33 | pattern: /^(?!(?:[a-zA-Z]:)|\/).+\.worker\.ts$/ 34 | }), 35 | resolve({ 36 | browser: true 37 | }), 38 | commonjs(), 39 | typescript({ 40 | clean: true, 41 | useTsconfigDeclarationDir: true, 42 | tsconfigOverride: { 43 | noEmit: false, 44 | sourceMap: true, 45 | compilerOptions: { 46 | lib: ['dom', 'es6'] 47 | } 48 | } 49 | }), 50 | replace({ 51 | 'process.env.NODE_ENV': `'${process.env.NODE_ENV}'`, 52 | preventAssignment: true 53 | }), 54 | shouldMinify && terser(), 55 | sourcemaps() 56 | ]; 57 | }; 58 | 59 | const getStatsPlugins = () => { 60 | if (!shouldGenerateStats) return []; 61 | return [visualizer(visualizerOptions), analyze({ summaryOnly: true })]; 62 | }; 63 | 64 | const footer = `('Auth0Client' in this) && this.console && this.console.warn && this.console.warn('Auth0Client already declared on the global namespace'); 65 | this && this.${EXPORT_NAME} && (this.Auth0Client = this.Auth0Client || this.${EXPORT_NAME}.Auth0Client);`; 66 | 67 | let bundles = [ 68 | { 69 | input: 'src/index.cjs.ts', 70 | output: { 71 | name: EXPORT_NAME, 72 | file: 'dist/auth0-vue.development.js', 73 | footer, 74 | format: 'umd', 75 | sourcemap: true, 76 | exports: 'default', 77 | globals: { 78 | vue: 'Vue', 79 | 'vue-router': 'VueRouter' 80 | } 81 | }, 82 | external: ['vue', 'vue-router'], 83 | plugins: [ 84 | ...getPlugins(false), 85 | !isProduction && 86 | dev({ 87 | dirs: ['dist', 'static', 'node_modules/vue/dist', 'playground'], 88 | port: serverPort, 89 | extend(app, modules) { 90 | app.use(modules.mount(createApp({ port: serverPort }))); 91 | } 92 | }), 93 | !isProduction && livereload() 94 | ], 95 | watch: { 96 | clearScreen: false 97 | }, 98 | external: ['vue', 'vue-router'] 99 | } 100 | ]; 101 | 102 | if (isProduction) { 103 | bundles = bundles.concat( 104 | { 105 | input: 'src/index.cjs.ts', 106 | output: [ 107 | { 108 | name: EXPORT_NAME, 109 | file: 'dist/auth0-vue.production.js', 110 | footer, 111 | format: 'umd', 112 | exports: 'default', 113 | globals: { 114 | vue: 'Vue', 115 | 'vue-router': 'VueRouter' 116 | } 117 | } 118 | ], 119 | external: ['vue', 'vue-router'], 120 | plugins: [...getPlugins(isProduction), ...getStatsPlugins()] 121 | }, 122 | { 123 | input: 'src/index.ts', 124 | output: [ 125 | { 126 | file: pkg.module, 127 | format: 'esm' 128 | } 129 | ], 130 | external: ['vue', 'vue-router'], 131 | plugins: getPlugins(isProduction) 132 | }, 133 | { 134 | input: 'src/index.cjs.ts', 135 | output: [ 136 | { 137 | name: EXPORT_NAME, 138 | file: pkg.main, 139 | format: 'cjs', 140 | exports: 'default' 141 | } 142 | ], 143 | plugins: getPlugins(false), 144 | external: Object.keys(pkg.dependencies) 145 | } 146 | ); 147 | } else { 148 | bundles = bundles.concat({ 149 | input: 'playground/index.ts', 150 | output: { 151 | format: 'umd', 152 | file: 'dist/auth0-vue.playground.js', 153 | globals: { 154 | vue: 'Vue', 155 | 'vue-router': 'VueRouter' 156 | } 157 | }, 158 | external: ['vue', 'vue-router'], 159 | plugins: [vue(), ...getPlugins(false)] 160 | }); 161 | } 162 | export default bundles; 163 | -------------------------------------------------------------------------------- /docs/types/Cacheable.html: -------------------------------------------------------------------------------- 1 | Cacheable | @auth0/auth0-vue

Type Alias Cacheable

Cacheable: WrappedCacheEntry | KeyManifestEntry
2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Auth0", 3 | "name": "@auth0/auth0-vue", 4 | "description": "Auth0 SDK for Vue Applications using Authorization Code Grant Flow with PKCE", 5 | "license": "MIT", 6 | "version": "2.5.0", 7 | "main": "dist/lib/auth0-vue.cjs.js", 8 | "types": "dist/typings/index.d.ts", 9 | "module": "dist/auth0-vue.production.esm.js", 10 | "exports": { 11 | ".": { 12 | "require": "./dist/lib/auth0-vue.cjs.js", 13 | "import": "./dist/auth0-vue.production.esm.js", 14 | "types": "./dist/typings/index.d.ts" 15 | } 16 | }, 17 | "scripts": { 18 | "dev": "rimraf dist && rollup -c --watch", 19 | "start": "npm run dev", 20 | "docs": "typedoc --options ./typedoc.js src", 21 | "build": "rimraf dist && rollup -m -c --environment NODE_ENV:production && npm run test:es-check", 22 | "build:stats": "npm run build -- --environment WITH_STATS:true && open stats.html", 23 | "lint": "eslint ./src --ext ts", 24 | "lint:security": "eslint ./src --ext ts --no-eslintrc --config ./.eslintrc.security", 25 | "test": "jest --coverage --silent", 26 | "test:watch": "jest --coverage --watch", 27 | "test:debug": "node --inspect node_modules/.bin/jest --runInBand", 28 | "test:open:integration": "cypress open", 29 | "test:watch:integration": "concurrently --raw npm:dev 'npm:test:open:integration'", 30 | "test:es-check": "npm run test:es-check:es2017 && npm run test:es-check:es2017:module", 31 | "test:es-check:es2017": "es-check es2017 dist/auth0-vue.production.js", 32 | "test:es-check:es2017:module": "es-check es2017 dist/auth0-vue.production.esm.js --module ", 33 | "test:integration:server": "npm run dev", 34 | "test:integration:tests": "wait-on http://localhost:3000/ && cypress run", 35 | "test:integration": "concurrently --raw --kill-others --success first npm:test:integration:server npm:test:integration:tests", 36 | "serve:coverage": "serve coverage/lcov-report -n", 37 | "serve:stats": "serve bundle-stats -n", 38 | "print-bundle-size": "node ./scripts/print-bundle-size", 39 | "prepack": "npm run build", 40 | "publish:cdn": "ccu --trace" 41 | }, 42 | "devDependencies": { 43 | "@auth0/component-cdn-uploader": "github:auth0/component-cdn-uploader#v2.2.2", 44 | "@jest/globals": "^29.3.1", 45 | "@rollup/plugin-replace": "^3.0.0", 46 | "@types/node": "^25.0.1", 47 | "@typescript-eslint/eslint-plugin": "^5.54.1", 48 | "@typescript-eslint/parser": "^5.54.1", 49 | "browserstack-cypress-cli": "^1.19.0", 50 | "cli-table": "^0.3.11", 51 | "concurrently": "^7.5.0", 52 | "cypress": "^13.1.0", 53 | "es-check": "^7.0.1", 54 | "eslint": "^8.5.0", 55 | "eslint-plugin-security": "^1.5.0", 56 | "gzip-size": "^6.0.0", 57 | "husky": "^7.0.4", 58 | "jest": "^29.3.1", 59 | "jest-environment-jsdom": "^29.3.1", 60 | "jest-junit": "^14.0.1", 61 | "oidc-provider": "^7.10.5", 62 | "prettier": "^2.5.1", 63 | "pretty-quick": "^3.1.3", 64 | "qss": "^2.0.3", 65 | "rimraf": "^3.0.2", 66 | "rollup": "^2.61.1", 67 | "rollup-plugin-analyzer": "^4.0.0", 68 | "rollup-plugin-commonjs": "^10.1.0", 69 | "rollup-plugin-dev": "^1.1.3", 70 | "rollup-plugin-livereload": "^2.0.5", 71 | "rollup-plugin-node-resolve": "^5.2.0", 72 | "rollup-plugin-sourcemaps": "^0.6.3", 73 | "rollup-plugin-terser": "^7.0.2", 74 | "rollup-plugin-typescript2": "^0.36.0", 75 | "rollup-plugin-visualizer": "^5.5.2", 76 | "rollup-plugin-vue": "^6.0.0", 77 | "rollup-plugin-web-worker-loader": "^1.6.1", 78 | "serve": "^14.1.1", 79 | "ts-jest": "^29.0.3", 80 | "tslib": "^2.3.1", 81 | "typedoc": "^0.26.11", 82 | "typescript": "^5.6.3", 83 | "vue-router": "^4.0.12", 84 | "wait-on": "^7.2.0" 85 | }, 86 | "dependencies": { 87 | "@auth0/auth0-spa-js": "^2.10.0", 88 | "vue": "^3.5.21" 89 | }, 90 | "peerDependencies": { 91 | "vue-router": "^4.0.12" 92 | }, 93 | "peerDependenciesMeta": { 94 | "vue-router": { 95 | "optional": true 96 | } 97 | }, 98 | "files": [ 99 | "src", 100 | "dist" 101 | ], 102 | "repository": { 103 | "type": "git", 104 | "url": "git://github.com/auth0/auth0-vue.git" 105 | }, 106 | "bugs": { 107 | "url": "https://github.com/auth0/auth0-vue/issues" 108 | }, 109 | "homepage": "https://github.com/auth0/auth0-vue#readme", 110 | "keywords": [ 111 | "auth0", 112 | "login", 113 | "Authorization Code Grant Flow", 114 | "PKCE", 115 | "Single Page Application authentication", 116 | "SPA authentication", 117 | "Vue" 118 | ], 119 | "ccu": { 120 | "name": "auth0-vue", 121 | "cdn": "https://cdn.auth0.com", 122 | "mainBundleFile": "auth0-vue.production.js", 123 | "bucket": "assets.us.auth0.com", 124 | "localPath": "dist", 125 | "digest": { 126 | "hashes": [ 127 | "sha384" 128 | ], 129 | "extensions": [ 130 | ".js" 131 | ] 132 | } 133 | }, 134 | "husky": { 135 | "hooks": { 136 | "pre-commit": "pretty-quick --staged" 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /docs/types/CacheLocation.html: -------------------------------------------------------------------------------- 1 | CacheLocation | @auth0/auth0-vue

Type Alias CacheLocation

CacheLocation: "memory" | "localstorage"

The possible locations where tokens can be stored

2 |
3 | -------------------------------------------------------------------------------- /docs/hierarchy.html: -------------------------------------------------------------------------------- 1 | @auth0/auth0-vue
2 | -------------------------------------------------------------------------------- /docs/types/CustomFetchMinimalOutput.html: -------------------------------------------------------------------------------- 1 | CustomFetchMinimalOutput | @auth0/auth0-vue

Type Alias CustomFetchMinimalOutput

CustomFetchMinimalOutput: {
    headers: ResponseHeaders;
    status: number;
}
2 | -------------------------------------------------------------------------------- /docs/variables/AUTH0_INJECTION_KEY.html: -------------------------------------------------------------------------------- 1 | AUTH0_INJECTION_KEY | @auth0/auth0-vue

Variable AUTH0_INJECTION_KEYConst

AUTH0_INJECTION_KEY: InjectionKey<Auth0VueClient> = ...

Injection token used to provide the Auth0VueClient instance. Can be used to pass to inject()

2 |
inject(AUTH0_INJECTION_KEY)
3 | 
4 | 5 |
6 | -------------------------------------------------------------------------------- /docs/functions/useAuth0.html: -------------------------------------------------------------------------------- 1 | useAuth0 | @auth0/auth0-vue

Function useAuth0

4 | -------------------------------------------------------------------------------- /docs/functions/authGuard.html: -------------------------------------------------------------------------------- 1 | authGuard | @auth0/auth0-vue

Function authGuard

  • Parameters

    • to: RouteLocation

    Returns Promise<boolean>

2 | -------------------------------------------------------------------------------- /docs/types/FetcherConfig.html: -------------------------------------------------------------------------------- 1 | FetcherConfig | @auth0/auth0-vue

Type Alias FetcherConfig<TOutput>

FetcherConfig<TOutput>: {
    baseUrl?: string;
    dpopNonceId?: string;
    fetch?: CustomFetchImpl<TOutput>;
    getAccessToken?: AccessTokenFactory;
}

Type Parameters

2 | -------------------------------------------------------------------------------- /docs/functions/createAuth0.html: -------------------------------------------------------------------------------- 1 | createAuth0 | @auth0/auth0-vue

Function createAuth0

  • Creates the Auth0 plugin.

    2 |

    Parameters

    Returns Auth0Plugin

    An instance of Auth0Plugin

    5 |
6 | -------------------------------------------------------------------------------- /cypress/fixtures/users.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "Leanne Graham", 5 | "username": "Bret", 6 | "email": "Sincere@april.biz", 7 | "address": { 8 | "street": "Kulas Light", 9 | "suite": "Apt. 556", 10 | "city": "Gwenborough", 11 | "zipcode": "92998-3874", 12 | "geo": { 13 | "lat": "-37.3159", 14 | "lng": "81.1496" 15 | } 16 | }, 17 | "phone": "1-770-736-8031 x56442", 18 | "website": "hildegard.org", 19 | "company": { 20 | "name": "Romaguera-Crona", 21 | "catchPhrase": "Multi-layered client-server neural-net", 22 | "bs": "harness real-time e-markets" 23 | } 24 | }, 25 | { 26 | "id": 2, 27 | "name": "Ervin Howell", 28 | "username": "Antonette", 29 | "email": "Shanna@melissa.tv", 30 | "address": { 31 | "street": "Victor Plains", 32 | "suite": "Suite 879", 33 | "city": "Wisokyburgh", 34 | "zipcode": "90566-7771", 35 | "geo": { 36 | "lat": "-43.9509", 37 | "lng": "-34.4618" 38 | } 39 | }, 40 | "phone": "010-692-6593 x09125", 41 | "website": "anastasia.net", 42 | "company": { 43 | "name": "Deckow-Crist", 44 | "catchPhrase": "Proactive didactic contingency", 45 | "bs": "synergize scalable supply-chains" 46 | } 47 | }, 48 | { 49 | "id": 3, 50 | "name": "Clementine Bauch", 51 | "username": "Samantha", 52 | "email": "Nathan@yesenia.net", 53 | "address": { 54 | "street": "Douglas Extension", 55 | "suite": "Suite 847", 56 | "city": "McKenziehaven", 57 | "zipcode": "59590-4157", 58 | "geo": { 59 | "lat": "-68.6102", 60 | "lng": "-47.0653" 61 | } 62 | }, 63 | "phone": "1-463-123-4447", 64 | "website": "ramiro.info", 65 | "company": { 66 | "name": "Romaguera-Jacobson", 67 | "catchPhrase": "Face to face bifurcated interface", 68 | "bs": "e-enable strategic applications" 69 | } 70 | }, 71 | { 72 | "id": 4, 73 | "name": "Patricia Lebsack", 74 | "username": "Karianne", 75 | "email": "Julianne.OConner@kory.org", 76 | "address": { 77 | "street": "Hoeger Mall", 78 | "suite": "Apt. 692", 79 | "city": "South Elvis", 80 | "zipcode": "53919-4257", 81 | "geo": { 82 | "lat": "29.4572", 83 | "lng": "-164.2990" 84 | } 85 | }, 86 | "phone": "493-170-9623 x156", 87 | "website": "kale.biz", 88 | "company": { 89 | "name": "Robel-Corkery", 90 | "catchPhrase": "Multi-tiered zero tolerance productivity", 91 | "bs": "transition cutting-edge web services" 92 | } 93 | }, 94 | { 95 | "id": 5, 96 | "name": "Chelsey Dietrich", 97 | "username": "Kamren", 98 | "email": "Lucio_Hettinger@annie.ca", 99 | "address": { 100 | "street": "Skiles Walks", 101 | "suite": "Suite 351", 102 | "city": "Roscoeview", 103 | "zipcode": "33263", 104 | "geo": { 105 | "lat": "-31.8129", 106 | "lng": "62.5342" 107 | } 108 | }, 109 | "phone": "(254)954-1289", 110 | "website": "demarco.info", 111 | "company": { 112 | "name": "Keebler LLC", 113 | "catchPhrase": "User-centric fault-tolerant solution", 114 | "bs": "revolutionize end-to-end systems" 115 | } 116 | }, 117 | { 118 | "id": 6, 119 | "name": "Mrs. Dennis Schulist", 120 | "username": "Leopoldo_Corkery", 121 | "email": "Karley_Dach@jasper.info", 122 | "address": { 123 | "street": "Norberto Crossing", 124 | "suite": "Apt. 950", 125 | "city": "South Christy", 126 | "zipcode": "23505-1337", 127 | "geo": { 128 | "lat": "-71.4197", 129 | "lng": "71.7478" 130 | } 131 | }, 132 | "phone": "1-477-935-8478 x6430", 133 | "website": "ola.org", 134 | "company": { 135 | "name": "Considine-Lockman", 136 | "catchPhrase": "Synchronised bottom-line interface", 137 | "bs": "e-enable innovative applications" 138 | } 139 | }, 140 | { 141 | "id": 7, 142 | "name": "Kurtis Weissnat", 143 | "username": "Elwyn.Skiles", 144 | "email": "Telly.Hoeger@billy.biz", 145 | "address": { 146 | "street": "Rex Trail", 147 | "suite": "Suite 280", 148 | "city": "Howemouth", 149 | "zipcode": "58804-1099", 150 | "geo": { 151 | "lat": "24.8918", 152 | "lng": "21.8984" 153 | } 154 | }, 155 | "phone": "210.067.6132", 156 | "website": "elvis.io", 157 | "company": { 158 | "name": "Johns Group", 159 | "catchPhrase": "Configurable multimedia task-force", 160 | "bs": "generate enterprise e-tailers" 161 | } 162 | }, 163 | { 164 | "id": 8, 165 | "name": "Nicholas Runolfsdottir V", 166 | "username": "Maxime_Nienow", 167 | "email": "Sherwood@rosamond.me", 168 | "address": { 169 | "street": "Ellsworth Summit", 170 | "suite": "Suite 729", 171 | "city": "Aliyaview", 172 | "zipcode": "45169", 173 | "geo": { 174 | "lat": "-14.3990", 175 | "lng": "-120.7677" 176 | } 177 | }, 178 | "phone": "586.493.6943 x140", 179 | "website": "jacynthe.com", 180 | "company": { 181 | "name": "Abernathy Group", 182 | "catchPhrase": "Implemented secondary concept", 183 | "bs": "e-enable extensible e-tailers" 184 | } 185 | }, 186 | { 187 | "id": 9, 188 | "name": "Glenna Reichert", 189 | "username": "Delphine", 190 | "email": "Chaim_McDermott@dana.io", 191 | "address": { 192 | "street": "Dayna Park", 193 | "suite": "Suite 449", 194 | "city": "Bartholomebury", 195 | "zipcode": "76495-3109", 196 | "geo": { 197 | "lat": "24.6463", 198 | "lng": "-168.8889" 199 | } 200 | }, 201 | "phone": "(775)976-6794 x41206", 202 | "website": "conrad.com", 203 | "company": { 204 | "name": "Yost and Sons", 205 | "catchPhrase": "Switchable contextually-based project", 206 | "bs": "aggregate real-time technologies" 207 | } 208 | }, 209 | { 210 | "id": 10, 211 | "name": "Clementina DuBuque", 212 | "username": "Moriah.Stanton", 213 | "email": "Rey.Padberg@karina.biz", 214 | "address": { 215 | "street": "Kattie Turnpike", 216 | "suite": "Suite 198", 217 | "city": "Lebsackbury", 218 | "zipcode": "31428-2261", 219 | "geo": { 220 | "lat": "-38.2386", 221 | "lng": "57.2232" 222 | } 223 | }, 224 | "phone": "024-648-3804", 225 | "website": "ambrose.net", 226 | "company": { 227 | "name": "Hoeger LLC", 228 | "catchPhrase": "Centralized empowering task-force", 229 | "bs": "target end-to-end models" 230 | } 231 | } 232 | ] 233 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Auth0 SDK for Vue 3 Single Page Applications](https://cdn.auth0.com/website/sdks/banners/auth0-vue-banner.png) 2 | 3 | ![Release](https://img.shields.io/npm/v/@auth0/auth0-vue) 4 | [![Codecov](https://codecov.io/gh/auth0/auth0-vue/branch/main/graph/badge.svg)](https://codecov.io/gh/auth0/auth0-vue) 5 | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/auth0/auth0-vue) 6 | ![Downloads](https://img.shields.io/npm/dw/@auth0/auth0-vue) 7 | [![License](https://img.shields.io/:license-mit-blue.svg?style=flat)](https://opensource.org/licenses/MIT) 8 | [![CircleCI](https://img.shields.io/circleci/build/github/auth0/auth0-vue)](https://circleci.com/gh/auth0/auth0-vue) 9 | 10 | 📚 [Documentation](#documentation) - 🚀 [Getting Started](#getting-started) - 💻 [API Reference](#api-reference) - 💬 [Feedback](#feedback) 11 | 12 | ## Documentation 13 | 14 | - [Quickstart](https://auth0.com/docs/quickstart/spa/vuejs) - our interactive guide for quickly adding login, logout and user information to a Vue 3 app using Auth0. 15 | - [Sample App](https://github.com/auth0-samples/auth0-vue-samples/tree/master/01-Login) - a full-fledged Vue 3 application integrated with Auth0. 16 | - [FAQs](https://github.com/auth0/auth0-vue/tree/main/FAQ.md) - frequently asked questions about the auth0-vue SDK. 17 | - [Examples](https://github.com/auth0/auth0-vue/tree/main/EXAMPLES.md) - code samples for common Vue 3 authentication scenario's. 18 | - [Docs site](https://www.auth0.com/docs) - explore our docs site and learn more about Auth0. 19 | 20 | ## Getting started 21 | 22 | ### Requirements 23 | 24 | This library supports **Vue 3** applications. 25 | For integrating Auth0 with a Vue 2 application, please read [the Vue 2 Tutorial](https://github.com/auth0/auth0-vue/blob/main/tutorial/vue2-login.md). 26 | 27 | ### Installation 28 | 29 | Using [npm](https://npmjs.org): 30 | 31 | ```sh 32 | npm install @auth0/auth0-vue 33 | ``` 34 | 35 | Using [yarn](https://yarnpkg.com): 36 | 37 | ```sh 38 | yarn add @auth0/auth0-vue 39 | ``` 40 | 41 | ### Configure Auth0 42 | 43 | Create a **Single Page Application** in the [Auth0 Dashboard](https://manage.auth0.com/#/applications). 44 | 45 | > **If you're using an existing application**, verify that you have configured the following settings in your Single Page Application: 46 | > 47 | > - Click on the "Settings" tab of your application's page. 48 | > - Scroll down and click on the "Show Advanced Settings" link. 49 | > - Under "Advanced Settings", click on the "OAuth" tab. 50 | > - Ensure that "JsonWebToken Signature Algorithm" is set to `RS256` and that "OIDC Conformant" is enabled. 51 | 52 | Next, configure the following URLs for your application under the "Application URIs" section of the "Settings" page: 53 | 54 | - **Allowed Callback URLs**: `http://localhost:3000` 55 | - **Allowed Logout URLs**: `http://localhost:3000` 56 | - **Allowed Web Origins**: `http://localhost:3000` 57 | 58 | > 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 (see below). 59 | 60 | Take note of the **Client ID** and **Domain** values under the "Basic Information" section. You'll need these values in the next step. 61 | 62 | ### Configure the SDK 63 | 64 | Create an instance of the `Auth0Plugin` by calling `createAuth0` and pass it to Vue's `app.use()`. 65 | 66 | ```js 67 | import { createAuth0 } from '@auth0/auth0-vue'; 68 | 69 | const app = createApp(App); 70 | 71 | app.use( 72 | createAuth0({ 73 | domain: '', 74 | clientId: '', 75 | authorizationParams: { 76 | redirect_uri: '' 77 | } 78 | }) 79 | ); 80 | 81 | app.mount('#app'); 82 | ``` 83 | 84 | ### Add login to your application 85 | 86 | In order to add login to your application you can use the `loginWithRedirect` function that is exposed on the return value of `useAuth0`, which you can access in your component's `setup` function. 87 | 88 | ```html 89 | 104 | ``` 105 | 106 | Once setup returns the correct method, you can call that method from your component's HTML. 107 | 108 | ```html 109 | 114 | ``` 115 | 116 |
117 | Using Options API 118 | 119 | ```html 120 | 125 | 126 | 135 | ``` 136 | 137 |
138 | 139 | For more code samples on how to integrate the **auth0-vue** SDK in your **Vue 3** application, have a look at our [examples](https://github.com/auth0/auth0-vue/tree/main/EXAMPLES.md). 140 | 141 | ## API reference 142 | 143 | Explore public API's available in auth0-vue. 144 | 145 | - [createAuth0](https://auth0.github.io/auth0-vue/functions/createAuth0.html) 146 | - [createAuthGuard](https://auth0.github.io/auth0-vue/functions/createAuthGuard.html) 147 | - [useAuth0](https://auth0.github.io/auth0-vue/functions/useAuth0.html) 148 | - [Auth0PluginOptions](https://auth0.github.io/auth0-vue/interfaces/Auth0PluginOptions.html) 149 | - [Auth0VueClientOptions](https://auth0.github.io/auth0-vue/interfaces/Auth0VueClientOptions.html) 150 | - [Auth0VueClient](https://auth0.github.io/auth0-vue/interfaces/Auth0VueClient.html) 151 | 152 | ## Feedback 153 | 154 | ### Contributing 155 | 156 | We appreciate feedback and contribution to this repo! Before you get started, please see the following: 157 | 158 | - [Auth0's general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md) 159 | - [Auth0's code of conduct guidelines](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md) 160 | - [This repo's contribution guide](https://github.com/auth0/auth0-vue/blob/main/CONTRIBUTING.md) 161 | 162 | ### Raise an issue 163 | 164 | To provide feedback or report a bug, please [raise an issue on our issue tracker](https://github.com/auth0/auth0-vue/issues). 165 | 166 | ### Vulnerability Reporting 167 | 168 | 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. 169 | 170 | --- 171 | 172 |

173 | 174 | 175 | 176 | Auth0 Logo 177 | 178 |

179 |

Auth0 is an easy to implement, adaptable authentication and authorization platform. To learn more checkout Why Auth0?

180 |

181 | This project is licensed under the MIT license. See the LICENSE file for more info.

-------------------------------------------------------------------------------- /__tests__/guard.test.ts: -------------------------------------------------------------------------------- 1 | import type { RouteLocation } from 'vue-router'; 2 | import { App, ref } from 'vue'; 3 | import { Auth0VueClient, authGuard, createAuthGuard } from '../src/index'; 4 | import { AUTH0_TOKEN } from '../src/token'; 5 | import { client } from './../src/plugin'; 6 | import { beforeEach, describe, expect, it, jest } from '@jest/globals'; 7 | import { RedirectLoginOptions } from '@auth0/auth0-spa-js'; 8 | 9 | let watchEffectMock; 10 | 11 | jest.mock('vue', () => { 12 | return { 13 | ...(jest.requireActual('vue') as any), 14 | watchEffect: function (cb) { 15 | watchEffectMock = cb; 16 | return () => {}; 17 | } 18 | }; 19 | }); 20 | 21 | jest.mock('./../src/plugin', () => { 22 | return { 23 | ...(jest.requireActual('./../src/plugin') as any), 24 | client: ref({ 25 | loginWithRedirect: jest.fn().mockResolvedValue({}), 26 | isAuthenticated: ref(false), 27 | isLoading: ref(false) 28 | }) 29 | }; 30 | }); 31 | 32 | describe('createAuthGuard', () => { 33 | let appMock: App; 34 | let auth0Mock: Auth0VueClient = { 35 | loginWithRedirect: jest.fn().mockResolvedValue({}), 36 | isAuthenticated: ref(false), 37 | isLoading: ref(false) 38 | } as unknown as Auth0VueClient; 39 | 40 | beforeEach(() => { 41 | auth0Mock.isAuthenticated.value = false; 42 | auth0Mock.isLoading.value = false; 43 | appMock = { 44 | config: { 45 | globalProperties: { 46 | [AUTH0_TOKEN]: auth0Mock 47 | } 48 | } 49 | } as any as App; 50 | }); 51 | 52 | it('should create the guard', async () => { 53 | const guard = createAuthGuard(appMock); 54 | 55 | auth0Mock.isAuthenticated.value = true; 56 | 57 | expect.assertions(2); 58 | 59 | const result = await guard({ 60 | fullPath: 'abc' 61 | } as any); 62 | 63 | expect(result).toBe(true); 64 | expect(auth0Mock.loginWithRedirect).not.toHaveBeenCalled(); 65 | }); 66 | 67 | it('should create the guard without app', async () => { 68 | const guard = createAuthGuard(); 69 | 70 | client.value!.isAuthenticated = true as any; 71 | 72 | expect.assertions(2); 73 | 74 | const result = await guard({ 75 | fullPath: 'abc' 76 | } as any); 77 | 78 | expect(result).toBe(true); 79 | expect(auth0Mock.loginWithRedirect).not.toHaveBeenCalled(); 80 | }); 81 | 82 | it('should create the guard with empty options', async () => { 83 | const guard = createAuthGuard({}); 84 | 85 | client.value!.isAuthenticated = true as any; 86 | 87 | expect.assertions(2); 88 | 89 | const result = await guard({ 90 | fullPath: 'abc' 91 | } as any); 92 | 93 | expect(result).toBe(true); 94 | expect(auth0Mock.loginWithRedirect).not.toHaveBeenCalled(); 95 | }); 96 | 97 | it('should create the guard with app in the options', async () => { 98 | const guard = createAuthGuard({ app: appMock }); 99 | expect(guard).toBeDefined(); 100 | expect(typeof guard).toBe('function'); 101 | }); 102 | 103 | it('should wait untill isLoading is false', async () => { 104 | const guard = createAuthGuard(appMock); 105 | 106 | auth0Mock.isLoading.value = true; 107 | 108 | expect.assertions(4); 109 | 110 | guard({ 111 | fullPath: 'abc' 112 | } as any).then(() => { 113 | expect(true).toBeTruthy(); 114 | }); 115 | 116 | expect(auth0Mock.loginWithRedirect).not.toHaveBeenCalled(); 117 | 118 | auth0Mock.isLoading.value = false; 119 | 120 | expect(auth0Mock.loginWithRedirect).not.toHaveBeenCalled(); 121 | 122 | await watchEffectMock(); 123 | 124 | expect(auth0Mock.loginWithRedirect).toHaveBeenCalled(); 125 | }); 126 | 127 | it('should return true when authenticated', async () => { 128 | const guard = createAuthGuard(appMock); 129 | 130 | auth0Mock.isAuthenticated.value = true; 131 | 132 | expect.assertions(2); 133 | 134 | const result = await guard({ 135 | fullPath: 'abc' 136 | } as any); 137 | 138 | expect(result).toBe(true); 139 | expect(auth0Mock.loginWithRedirect).not.toHaveBeenCalled(); 140 | }); 141 | 142 | it('should call loginWithRedirect', async () => { 143 | const guard = createAuthGuard(appMock); 144 | 145 | expect.assertions(1); 146 | 147 | await guard({ 148 | fullPath: 'abc' 149 | } as any); 150 | 151 | expect(auth0Mock.loginWithRedirect).toHaveBeenCalledWith( 152 | expect.objectContaining({ 153 | appState: { target: 'abc' } 154 | }) 155 | ); 156 | }); 157 | 158 | it('should call loginWithRedirect with RedirectLoginOptions and use default appState value', async () => { 159 | const guard = createAuthGuard({ 160 | app: appMock, 161 | redirectLoginOptions: { 162 | authorizationParams: { 163 | redirect_uri: '/custom_redirect' 164 | } 165 | } as RedirectLoginOptions 166 | }); 167 | 168 | expect.assertions(1); 169 | 170 | await guard({ 171 | fullPath: 'abc' 172 | } as RouteLocation); 173 | 174 | expect(auth0Mock.loginWithRedirect).toHaveBeenCalledWith( 175 | expect.objectContaining({ 176 | appState: { target: 'abc' }, 177 | authorizationParams: { 178 | redirect_uri: '/custom_redirect' 179 | } 180 | }) 181 | ); 182 | }); 183 | it('should call loginWithRedirect with RedirectLoginOptions and use provided appState value', async () => { 184 | const guard = createAuthGuard({ 185 | app: appMock, 186 | redirectLoginOptions: { 187 | appState: { target: '123' }, 188 | authorizationParams: { 189 | redirect_uri: '/custom_redirect2' 190 | } 191 | } as RedirectLoginOptions 192 | }); 193 | 194 | expect.assertions(1); 195 | 196 | await guard({ 197 | fullPath: 'abc' 198 | } as RouteLocation); 199 | 200 | expect(auth0Mock.loginWithRedirect).toHaveBeenCalledWith( 201 | expect.objectContaining({ 202 | appState: { target: '123' }, 203 | authorizationParams: { 204 | redirect_uri: '/custom_redirect2' 205 | } 206 | }) 207 | ); 208 | }); 209 | }); 210 | describe('authGuard', () => { 211 | let auth0Mock; 212 | 213 | beforeEach(() => { 214 | client.value!.isAuthenticated = false as any; 215 | client.value!.isLoading = false as any; 216 | auth0Mock = client.value; 217 | }); 218 | 219 | it('should wait untill isLoading is false', async () => { 220 | const guard = authGuard; 221 | 222 | auth0Mock.isLoading = true; 223 | 224 | expect.assertions(4); 225 | 226 | guard({ 227 | fullPath: 'abc' 228 | } as any).then(() => { 229 | expect(true).toBeTruthy(); 230 | }); 231 | 232 | expect(auth0Mock.loginWithRedirect).not.toHaveBeenCalled(); 233 | 234 | auth0Mock.isLoading = false; 235 | 236 | expect(auth0Mock.loginWithRedirect).not.toHaveBeenCalled(); 237 | 238 | await watchEffectMock(); 239 | 240 | expect(auth0Mock.loginWithRedirect).toHaveBeenCalled(); 241 | }); 242 | 243 | it('should return true when authenticated', async () => { 244 | const guard = authGuard; 245 | 246 | auth0Mock.isAuthenticated = true; 247 | 248 | expect.assertions(2); 249 | 250 | const result = await guard({ 251 | fullPath: 'abc' 252 | } as any); 253 | 254 | expect(result).toBe(true); 255 | expect(auth0Mock.loginWithRedirect).not.toHaveBeenCalled(); 256 | }); 257 | 258 | it('should call loginWithRedirect', async () => { 259 | const guard = authGuard; 260 | 261 | expect.assertions(1); 262 | 263 | await guard({ 264 | fullPath: 'abc' 265 | } as any); 266 | 267 | expect(auth0Mock.loginWithRedirect).toHaveBeenCalledWith( 268 | expect.objectContaining({ 269 | appState: { target: 'abc' } 270 | }) 271 | ); 272 | }); 273 | }); 274 | -------------------------------------------------------------------------------- /src/plugin.ts: -------------------------------------------------------------------------------- 1 | import type { App, Ref } from 'vue'; 2 | import { ref } from 'vue'; 3 | import type { Router } from 'vue-router'; 4 | import type { 5 | AppState, 6 | Auth0PluginOptions, 7 | Auth0VueClient, 8 | Auth0VueClientOptions, 9 | LogoutOptions, 10 | RedirectLoginOptions 11 | } from './interfaces'; 12 | import { AUTH0_INJECTION_KEY, AUTH0_TOKEN } from './token'; 13 | import version from './version'; 14 | import type { 15 | GetTokenSilentlyOptions, 16 | GetTokenSilentlyVerboseResponse, 17 | GetTokenWithPopupOptions, 18 | IdToken, 19 | PopupConfigOptions, 20 | PopupLoginOptions, 21 | RedirectLoginResult, 22 | ConnectAccountRedirectResult, 23 | FetcherConfig, 24 | Fetcher, 25 | CustomFetchMinimalOutput 26 | } from '@auth0/auth0-spa-js'; 27 | import { Auth0Client, User } from '@auth0/auth0-spa-js'; 28 | import { bindPluginMethods, deprecateRedirectUri } from './utils'; 29 | 30 | /** 31 | * Helper callback that's used by default before the plugin is installed. 32 | */ 33 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 34 | const PLUGIN_NOT_INSTALLED_HANDLER: any = () => { 35 | console.error(`Please ensure Auth0's Vue plugin is correctly installed.`); 36 | }; 37 | 38 | /** 39 | * Helper client that's used by default before the plugin is installed. 40 | */ 41 | const PLUGIN_NOT_INSTALLED_CLIENT: Auth0VueClient = { 42 | isLoading: ref(false), 43 | isAuthenticated: ref(false), 44 | user: ref(undefined), 45 | idTokenClaims: ref(undefined), 46 | error: ref(null), 47 | loginWithPopup: PLUGIN_NOT_INSTALLED_HANDLER, 48 | loginWithRedirect: PLUGIN_NOT_INSTALLED_HANDLER, 49 | getAccessTokenSilently: PLUGIN_NOT_INSTALLED_HANDLER, 50 | getAccessTokenWithPopup: PLUGIN_NOT_INSTALLED_HANDLER, 51 | logout: PLUGIN_NOT_INSTALLED_HANDLER, 52 | checkSession: PLUGIN_NOT_INSTALLED_HANDLER, 53 | handleRedirectCallback: PLUGIN_NOT_INSTALLED_HANDLER, 54 | getDpopNonce: PLUGIN_NOT_INSTALLED_HANDLER, 55 | setDpopNonce: PLUGIN_NOT_INSTALLED_HANDLER, 56 | generateDpopProof: PLUGIN_NOT_INSTALLED_HANDLER, 57 | createFetcher: PLUGIN_NOT_INSTALLED_HANDLER 58 | }; 59 | 60 | /** 61 | * @ignore 62 | */ 63 | export const client: Ref = ref( 64 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 65 | PLUGIN_NOT_INSTALLED_CLIENT as any 66 | ); 67 | 68 | /** 69 | * @ignore 70 | */ 71 | export class Auth0Plugin implements Auth0VueClient { 72 | private _client!: Auth0Client; 73 | public isLoading: Ref = ref(true); 74 | public isAuthenticated: Ref = ref(false); 75 | public user: Ref = ref({}); 76 | public idTokenClaims = ref(); 77 | public error = ref(null); 78 | 79 | constructor( 80 | private clientOptions: Auth0VueClientOptions, 81 | private pluginOptions?: Auth0PluginOptions 82 | ) { 83 | // Vue Plugins can have issues when passing around the instance to `provide` 84 | // Therefor we need to bind all methods correctly to `this`. 85 | bindPluginMethods(this, ['constructor']); 86 | } 87 | 88 | install(app: App) { 89 | this._client = new Auth0Client({ 90 | ...this.clientOptions, 91 | auth0Client: { 92 | name: 'auth0-vue', 93 | version: version 94 | } 95 | }); 96 | 97 | this.__checkSession(app.config.globalProperties.$router); 98 | 99 | // eslint-disable-next-line security/detect-object-injection 100 | app.config.globalProperties[AUTH0_TOKEN] = this; 101 | app.provide(AUTH0_INJECTION_KEY, this as Auth0VueClient); 102 | 103 | client.value = this as Auth0VueClient; 104 | } 105 | 106 | async loginWithRedirect(options?: RedirectLoginOptions) { 107 | deprecateRedirectUri(options); 108 | return this._client.loginWithRedirect(options); 109 | } 110 | 111 | async loginWithPopup( 112 | options?: PopupLoginOptions, 113 | config?: PopupConfigOptions 114 | ) { 115 | deprecateRedirectUri(options); 116 | return this.__proxy(() => this._client.loginWithPopup(options, config)); 117 | } 118 | 119 | async logout(options?: LogoutOptions) { 120 | if (options?.openUrl || options?.openUrl === false) { 121 | return this.__proxy(() => this._client.logout(options)); 122 | } 123 | 124 | return this._client.logout(options); 125 | } 126 | 127 | /* istanbul ignore next */ 128 | async getAccessTokenSilently( 129 | options: GetTokenSilentlyOptions & { detailedResponse: true } 130 | ): Promise; 131 | /* istanbul ignore next */ 132 | async getAccessTokenSilently( 133 | options?: GetTokenSilentlyOptions 134 | ): Promise; 135 | /* istanbul ignore next */ 136 | async getAccessTokenSilently( 137 | options: GetTokenSilentlyOptions = {} 138 | ): Promise { 139 | deprecateRedirectUri(options); 140 | return this.__proxy(() => this._client.getTokenSilently(options)); 141 | } 142 | 143 | async getAccessTokenWithPopup( 144 | options?: GetTokenWithPopupOptions, 145 | config?: PopupConfigOptions 146 | ) { 147 | deprecateRedirectUri(options); 148 | return this.__proxy(() => this._client.getTokenWithPopup(options, config)); 149 | } 150 | 151 | async checkSession(options?: GetTokenSilentlyOptions) { 152 | return this.__proxy(() => this._client.checkSession(options)); 153 | } 154 | 155 | async handleRedirectCallback( 156 | url?: string 157 | ): Promise< 158 | RedirectLoginResult | ConnectAccountRedirectResult 159 | > { 160 | return this.__proxy(() => 161 | this._client.handleRedirectCallback(url) 162 | ); 163 | } 164 | 165 | async getDpopNonce(id?: string): Promise { 166 | return this.__proxy(() => this._client.getDpopNonce(id)); 167 | } 168 | 169 | async setDpopNonce(nonce: string, id?: string): Promise { 170 | return this.__proxy(() => this._client.setDpopNonce(nonce, id)); 171 | } 172 | 173 | async generateDpopProof(params: { 174 | url: string; 175 | method: string; 176 | accessToken: string; 177 | nonce?: string; 178 | }): Promise { 179 | return this.__proxy(() => this._client.generateDpopProof(params)); 180 | } 181 | 182 | createFetcher( 183 | config?: FetcherConfig 184 | ): Fetcher { 185 | return this._client.createFetcher(config); 186 | } 187 | 188 | private async __checkSession(router?: Router) { 189 | const search = window.location.search; 190 | 191 | try { 192 | if ( 193 | (search.includes('code=') || search.includes('error=')) && 194 | search.includes('state=') && 195 | !this.pluginOptions?.skipRedirectCallback 196 | ) { 197 | const result = await this.handleRedirectCallback(); 198 | const appState = result?.appState; 199 | const target = appState?.target ?? '/'; 200 | 201 | window.history.replaceState({}, '', '/'); 202 | 203 | if (router) { 204 | router.push(target); 205 | } 206 | 207 | return result; 208 | } else { 209 | await this.checkSession(); 210 | } 211 | } catch (e) { 212 | // __checkSession should never throw an exception as it will fail installing the plugin. 213 | // Instead, errors during __checkSession are propagated using the errors property on `useAuth0`. 214 | 215 | window.history.replaceState({}, '', '/'); 216 | 217 | if (router) { 218 | router.push(this.pluginOptions?.errorPath || '/'); 219 | } 220 | } 221 | } 222 | 223 | private async __refreshState() { 224 | this.isAuthenticated.value = await this._client.isAuthenticated(); 225 | this.user.value = await this._client.getUser(); 226 | this.idTokenClaims.value = await this._client.getIdTokenClaims(); 227 | this.isLoading.value = false; 228 | } 229 | 230 | private async __proxy(cb: () => T, refreshState = true) { 231 | let result; 232 | try { 233 | result = await cb(); 234 | this.error.value = null; 235 | } catch (e) { 236 | this.error.value = e as Error; 237 | throw e; 238 | } finally { 239 | if (refreshState) { 240 | await this.__refreshState(); 241 | } 242 | } 243 | 244 | return result; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /docs/interfaces/AppState.html: -------------------------------------------------------------------------------- 1 | AppState | @auth0/auth0-vue

Interface AppState

interface AppState {
    target?: string;
    [key: string]: any;
}

Indexable

  • [key: string]: any

    Any custom parameter to be stored in appState

    2 |

Properties

target? 3 |

Properties

target?: string

Target path the app gets routed to after 4 | handling the callback from Auth0 (defaults to '/')

5 |
6 | -------------------------------------------------------------------------------- /docs/interfaces/PopupLoginOptions.html: -------------------------------------------------------------------------------- 1 | PopupLoginOptions | @auth0/auth0-vue

Interface PopupLoginOptions

interface PopupLoginOptions {
    authorizationParams?: AuthorizationParams;
}

Hierarchy (view full)

Properties

Properties

authorizationParams?: AuthorizationParams

URL parameters that will be sent back to the Authorization Server. This can be known parameters 3 | defined by Auth0 or custom parameters that you define.

4 |
5 | -------------------------------------------------------------------------------- /docs/assets/icons.svg: -------------------------------------------------------------------------------- 1 | MMNEPVFCICPMFPCPTTAAATR -------------------------------------------------------------------------------- /docs/classes/InMemoryCache.html: -------------------------------------------------------------------------------- 1 | InMemoryCache | @auth0/auth0-vue

Constructors

Properties

Constructors

Properties

enclosedCache: ICache
4 | -------------------------------------------------------------------------------- /docs/interfaces/PopupConfigOptions.html: -------------------------------------------------------------------------------- 1 | PopupConfigOptions | @auth0/auth0-vue

Interface PopupConfigOptions

interface PopupConfigOptions {
    popup?: any;
    timeoutInSeconds?: number;
}

Properties

Properties

popup?: any

Accepts an already-created popup window to use. If not specified, the SDK 4 | will create its own. This may be useful for platforms like iOS that have 5 | security restrictions around when popups can be invoked (e.g. from a user click event)

6 |
timeoutInSeconds?: number

The number of seconds to wait for a popup response before 7 | throwing a timeout error. Defaults to 60s

8 |
9 | --------------------------------------------------------------------------------