[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[*.{yml,yaml}]\nindent_size = 2\n\n[docker-compose.yml]\nindent_size = 4\n"
  },
  {
    "path": ".gitattributes",
    "content": ".editorconfig          export-ignore\n.gitattributes         export-ignore\n.github/               export-ignore\n.gitignore             export-ignore\n.php-cs-fixer.dist.php export-ignore\n.semgrepignore         export-ignore\n.shiprc                export-ignore\nCHANGELOG.ARCHIVE.md   export-ignore\nCHANGELOG.md           export-ignore\ndocs/                  export-ignore\nEXAMPLES.md            export-ignore\nexamples/              export-ignore\nopslevel.yml           export-ignore\nphpstan.neon.dist      export-ignore\nphpunit.xml.dist       export-ignore\npsalm.xml.dist         export-ignore\nrector.php             export-ignore\ntests/                 export-ignore\nUPGRADE.md             export-ignore\nvendor/                export-ignore\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "*\t@auth0/project-dx-sdks-engineer-codeowner\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\nBefore making any contributions to this repo, please review Auth0's [Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md). By contributing, you agree to uphold this code.\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contribution Guide\n\n-   [Getting Involved](#getting-involved)\n-   [Support Questions](#support-questions)\n-   [Code Contributions](#code-contributions)\n-   [Security Vulnerabilities](#security-vulnerabilities)\n-   [Coding Style](#coding-style)\n    -   [PHPDoc](#phpdoc)\n-   [Code of Conduct](#code-of-conduct)\n\n## Getting Involved\n\nTo encourage active collaboration, Auth0 strongly encourages pull requests, not just bug reports. Pull requests will only be reviewed when marked as \"ready for review\" (not in the \"draft\" state) and all tests for new features are passing. Lingering, non-active pull requests left in the \"draft\" state will eventually be closed.\n\nIf you file a bug report, your issue should contain a title and a clear description of the issue. You should also include as much relevant information as possible and a code sample that demonstrates the issue. The goal of a bug report is to make it easy for yourself - and others - to replicate the bug and develop a fix.\n\nRemember, bug reports are created in the hope that others with the same problem will be able to collaborate with you on solving it. Do not expect that the bug report will automatically see any activity or that others will jump to fix it. Creating a bug report serves to help you and others start on the path of fixing the problem. If you want to chip in, you can help out by fixing any bugs listed in our issue trackers.\n\n## Support Questions\n\nAuth0's GitHub issue trackers are not intended to provide integration support. Instead, please refer your questions to the [Auth0 Community](https://community.auth0.com).\n\n## Code Contributions\n\nYou may propose new features or improvements to existing SDK behavior by creating a feature request within the repository's issue tracker. If you are willing to implement at least some of the code that would be needed to complete the feature, please fork the repository and submit a pull request.\n\nAll development should be done in individual forks using dedicated branches, and submitted against the `main` default branch.\n\nPull request titles must follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) rules so our changelogs can be automatically generated. Commits messages are irrelevant as they will be squashed into the Pull request's title during a merge.\n\nThe following types are allowed:\n\n-   _feat:_ A new feature\n-   _perf:_ A code change that improves performance\n-   _refactor:_ A code change that neither fixes a bug nor adds a feature\n-   _build:_ Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)\n-   _ci:_ Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)\n-   _style:_ Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc)\n-   _fix:_ A bug fix\n-   _security:_ A change that improves security\n-   _docs:_ Documentation only changes\n-   _test:_ Adding missing tests or correcting existing tests\n\n## Security Vulnerabilities\n\nIf you discover a security vulnerability within this SDK, please review Auth0's [Responsible Disclosure Program](https://auth0.com/responsible-disclosure-policy) details the procedure for disclosing security issues. All security vulnerabilities will be promptly addressed.\n\n## Unit Testing and 100% Minimum Coverage\n\nWe use [PEST](https://pestphp.com/) for testing. You can run `composer pest` to run the test suite. You can also run `composer pest:coverage` to generate a code coverage report.\n\nWe require 100% code coverage for all new features. If you are adding a new feature, please add tests to cover all of the new code. If you are fixing a bug, please add a test that reproduces the bug and then shows that it has been fixed.\n\nPull requests that do not meet the minimum coverage requirements will not be merged.\n\n## Static Analysis\n\nWe use [PHPStan](https://phpstan.org) and [Psalm](https://psalm.dev/) for static analysis. You can use `composer phpstan` and `composer psalm` to run them.\n\n## Coding Style\n\nWe use [PHP CS Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) to ensure that code styling is consistent. You can run `composer phpcs` to check for any code style issues. `composer phpcs:fix` will attempt to automatically fix the issues, but be cautious as it may not always get it right.\n\nWe also use [Rector](https://github.com/rectorphp/rector) to catch edge cases where more optimal refactoring can be made. You can run `composer rector` to check for any recommendations, and `composer rector:fix` to accept the suggestions.\n\nIt's important to note that our GitHub CI will also run these checks for pull requests, but you should run these locally first to avoid any surprises when you push your code. If you disagree with one of these recommendations, please bring it up in the pull request so we can discuss it. We may decide to adjust the styling rules if we feel it's warranted, but we prefer to avoid it if possible.\n\n### PHPDoc\n\nAll public methods and classes should be documented with PHPDoc blocks.\n\nBelow is an example of a valid documentation block. Note that the @param attribute is followed by two spaces, the argument type, two more spaces, and finally the variable name:\n\n```php\n/**\n * Register a binding with the container.\n *\n * @param  string|array  $abstract\n * @param  \\Closure|string|null  $concrete\n * @param  bool  $shared\n * @return void\n *\n * @throws \\Exception\n */\npublic function bind($abstract, $concrete = null, $shared = false)\n{\n    //\n}\n```\n\n## Code of Conduct\n\nBefore making any contributions to this repo, please review Auth0's [Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md). By contributing, you agree to uphold this code.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Report a Bug\ndescription: Encountering unexpected problems or unintended behavior? Let us know!\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        **Please do not report security vulnerabilities here**. The [Responsible Disclosure Program](https://auth0.com/responsible-disclosure-policy) details the procedure for disclosing security issues.\n\n  - type: checkboxes\n    id: checklist\n    attributes:\n      label: Checklist\n      options:\n        - label: This can be reproduced using [the quickstart sample application](https://github.com/auth0-samples/laravel).\n          required: true\n        - label: I have looked at [the README](https://github.com/auth0/laravel-auth0/#readme) and have not found a solution.\n          required: true\n        - label: I have looked at [the `docs` directory](https://github.com/auth0/laravel-auth0/blob/main/docs) and have not found a solution.\n          required: true\n        - label: I have searched [previous issues](https://github.com/auth0/laravel-auth0/issues) and have not found a solution.\n          required: true\n        - label: I have searched [the Auth0 Community](https://community.auth0.com/tag/laravel) and have not found a solution.\n          required: true\n        - label: I agree to uphold [the Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md).\n          required: true\n\n  - type: dropdown\n    id: laravel\n    attributes:\n      label: Laravel Version\n      description: What version of Laravel are you using? (`composer show | grep laravel/framework`)\n      options:\n        - 10\n        - 9\n        - Other (specify below)\n    validations:\n      required: true\n\n  - type: dropdown\n    id: sdk\n    attributes:\n      label: SDK Version\n      description: What version of our SDK are you using? (`composer show | grep auth0/login`)\n      options:\n        - 7.13\n        - 7.12\n        - 7.11\n        - 7.10\n        - 7.9\n        - 7.8\n        - 7.7\n        - 7.6\n        - 7.5\n        - 7.4\n        - 7.3\n        - 7.2\n        - 7.1\n        - 7.0\n        - Other (specify below)\n    validations:\n      required: true\n\n  - type: dropdown\n    id: php\n    attributes:\n      label: PHP Version\n      description: What version of PHP are you running? (`php -v`)\n      options:\n        - PHP 8.3\n        - PHP 8.2\n        - Other (specify below)\n    validations:\n      required: true\n\n  - type: textarea\n    id: bug-description\n    attributes:\n      label: Description\n      description: Provide a description of the issue, including what you expected to happen.\n    validations:\n      required: true\n\n  - type: textarea\n    id: bug-reproduction\n    attributes:\n      label: How can we reproduce this issue?\n      description: Detail the steps taken to reproduce this error. If possible, please provide a GitHub repository to demonstrate the issue.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Report Auth0-PHP Issues\n    url: https://github.com/auth0/auth0-PHP/\n    about: For issues relating to the Auth0-PHP SDK, please report them in that repository.\n  - name: Community Support\n    url: https://community.auth0.com/tag/laravel\n    about: Please ask general usage questions here.\n  - name: Responsible Disclosure Program\n    url: https://auth0.com/whitehat\n    about: Please do not report security vulnerabilities on the public GitHub issue tracker. The Responsible Disclosure Program details the procedure for disclosing security issues.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Suggest a Feature\ndescription: Help us improve the SDK by suggest new features and improvements.\n\nbody:\n  - type: markdown\n    attributes:\n      value: Thanks for taking the time to help us improve this SDK!\n\n  - type: checkboxes\n    id: checklist\n    attributes:\n      label: Checklist\n      options:\n        - label: I agree to uphold [the Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md).\n          required: true\n\n  - type: textarea\n    id: feature-description\n    attributes:\n      label: Description\n      description: Please provide a summary of the change you'd like considered, including any relevant context.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\n  Please only send pull requests to branches that are actively supported.\n  Pull requests without an adequate title, description, or tests will be closed.\n-->\n\n### Changes\n\n<!--\n  What is changing, and why this is important?\n-->\n\n### References\n\n<!--\n  Link to any associated issues.\n-->\n\n### Testing\n\n<!--\n  Tests must be added for new functionality, and existing tests should complete without errors.\n  100% test coverage is required.\n-->\n\n### Contributor Checklist\n\n-   [ ] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)\n-   [ ] I have read the [Auth0 code of conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)\n"
  },
  {
    "path": ".github/SECURITY.md",
    "content": "# Security Policy\n\n**PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, [SEE BELOW](#reporting-a-vulnerability).**\n\n## Supported Versions\n\nPlease see [our support policy](https://github.com/auth0/laravel-auth0#requirements) for information on supported versions for security releases.\n\n## Reporting a Vulnerability\n\nIf you discover a security vulnerability within this SDK, please review Auth0's [Responsible Disclosure Program](https://auth0.com/responsible-disclosure-policy) details the procedure for disclosing security issues. All security vulnerabilities will be promptly addressed.\n"
  },
  {
    "path": ".github/SUPPORT.md",
    "content": "# Support Questions\n\nAuth0's GitHub issue trackers are not intended to provide integration support. Instead, please refer your questions to the [Auth0 Community](https://community.auth0.com).\n"
  },
  {
    "path": ".github/actions/get-prerelease/action.yml",
    "content": "name: Return a boolean indicating if the version contains prerelease identifiers\n\n#\n# Returns a simple true/false boolean indicating whether the version indicates it's a prerelease or not.\n#\n# TODO: Remove once the common repo is public.\n#\n\ninputs:\n  version:\n    required: true\n\noutputs:\n  prerelease:\n    value: ${{ steps.get_prerelease.outputs.PRERELEASE }}\n\nruns:\n  using: composite\n\n  steps:\n    - id: get_prerelease\n      shell: bash\n      run: |\n        if [[ \"${VERSION}\" == *\"beta\"* || \"${VERSION}\" == *\"alpha\"* ]]; then\n          echo \"PRERELEASE=true\" >> $GITHUB_OUTPUT\n        else\n          echo \"PRERELEASE=false\" >> $GITHUB_OUTPUT\n        fi\n      env:\n        VERSION: ${{ inputs.version }}\n"
  },
  {
    "path": ".github/actions/get-version/action.yml",
    "content": "name: Return the version extracted from the branch name\n\n#\n# Returns the version from a branch name of a pull request. 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.\n#\n# TODO: Remove once the common repo is public.\n#\n\noutputs:\n  version:\n    value: ${{ steps.get_version.outputs.VERSION }}\n\nruns:\n  using: composite\n\n  steps:\n    - id: get_version\n      shell: bash\n      run: |\n        VERSION=$(head -1 .version)\n        echo \"VERSION=${VERSION}\" >> $GITHUB_OUTPUT\n"
  },
  {
    "path": ".github/actions/publish-package/action.yml",
    "content": "name: Publish release to package manager\n\ninputs:\n  token:\n    required: true\n  files:\n    required: false\n  name:\n    required: true\n  body:\n    required: true\n  tag:\n    required: true\n  commit:\n    required: true\n  draft:\n    default: false\n    required: false\n  prerelease:\n    default: false\n    required: false\n\nruns:\n  using: composite\n\n  steps:\n    # Nothing to do for PHP.\n    - run: exit 0\n      shell: bash\n"
  },
  {
    "path": ".github/actions/release-create/action.yml",
    "content": "name: Create a GitHub release\n\n#\n# Creates a GitHub release with the given version.\n#\n# TODO: Remove once the common repo is public.\n#\n\ninputs:\n  token:\n    required: true\n  files:\n    required: false\n  name:\n    required: true\n  body:\n    required: true\n  tag:\n    required: true\n  commit:\n    required: true\n  draft:\n    default: false\n    required: false\n  prerelease:\n    default: false\n    required: false\n  fail_on_unmatched_files:\n    default: true\n    required: false\n\nruns:\n  using: composite\n\n  steps:\n    - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844\n      with:\n        body: ${{ inputs.body }}\n        name: ${{ inputs.name }}\n        tag_name: ${{ inputs.tag }}\n        target_commitish: ${{ inputs.commit }}\n        draft: ${{ inputs.draft }}\n        prerelease: ${{ inputs.prerelease }}\n        fail_on_unmatched_files: ${{ inputs.fail_on_unmatched_files }}\n        files: ${{ inputs.files }}\n      env:\n        GITHUB_TOKEN: ${{ inputs.token }}\n"
  },
  {
    "path": ".github/actions/rl-scanner/action.yml",
    "content": "name: 'Reversing Labs Scanner'\ndescription: 'Runs the Reversing Labs scanner on a specified artifact.'\ninputs:\n  artifact-path:\n    description: 'Path to the artifact to be scanned.'\n    required: true\n  version:\n    description: 'Version of the artifact.'\n    required: true\n\nruns:\n  using: 'composite'\n  steps:\n    - name: Set up Python\n      uses: actions/setup-python@v4\n      with:\n        python-version: '3.10'\n\n    - name: Install Python dependencies\n      shell: bash\n      run: |\n        pip install boto3 requests\n\n    - name: Configure AWS credentials\n      uses: aws-actions/configure-aws-credentials@v1\n      with:\n        role-to-assume: ${{ env.PRODSEC_TOOLS_ARN }}\n        aws-region: us-east-1\n        mask-aws-account-id: true\n\n    - name: Install RL Wrapper\n      shell: bash\n      run: |\n        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\"\n\n    - name: Run RL Scanner\n      shell: bash\n      env:\n        RLSECURE_LICENSE: ${{ env.RLSECURE_LICENSE }}\n        RLSECURE_SITE_KEY: ${{ env.RLSECURE_SITE_KEY }}\n        SIGNAL_HANDLER_TOKEN: ${{ env.SIGNAL_HANDLER_TOKEN }}\n        PYTHONUNBUFFERED: 1\n      run: |\n        if [ ! -f \"${{ inputs.artifact-path }}\" ]; then\n          echo \"Artifact not found: ${{ inputs.artifact-path }}\"\n          exit 1\n        fi\n\n        rl-wrapper \\\n          --artifact \"${{ inputs.artifact-path }}\" \\\n          --name \"${{ github.event.repository.name }}\" \\\n          --version \"${{ inputs.version }}\" \\\n          --repository \"${{ github.repository }}\" \\\n          --commit \"${{ github.sha }}\" \\\n          --build-env \"github_actions\" \\\n          --suppress_output\n\n        # Check the outcome of the scanner\n        if [ $? -ne 0 ]; then\n          echo \"RL Scanner failed.\"\n          echo \"scan-status=failed\" >> $GITHUB_ENV\n          exit 1\n        else\n          echo \"RL Scanner passed.\"\n          echo \"scan-status=success\" >> $GITHUB_ENV\n        fi\n\noutputs:\n  scan-status:\n    description: 'The outcome of the scan process.'\n    value: ${{ env.scan-status }}"
  },
  {
    "path": ".github/actions/setup/action.yml",
    "content": "name: Prepare PHP\ndescription: Prepare the PHP environment\n\ninputs:\n  php:\n    description: The PHP version to use\n    required: true\n  coverage:\n    description: The coverage extension to use\n    required: false\n    default: 'none'\n  extensions:\n    description: The PHP extensions to use\n    required: false\n    default: 'none, mbstring, curl, simplexml, dom, xmlwriter, xml, tokenizer, fileinfo, pdo'\n  runner:\n    description: The runner OS\n    required: false\n    default: 'ubuntu-latest'\n\nruns:\n  using: composite\n\n  steps:\n    - name: Setup PHP\n      uses: shivammathur/setup-php@4bd44f22a98a19e0950cbad5f31095157cc9621b # pin@2.25.4\n      with:\n        php-version: ${{ inputs.php }}\n        extensions: ${{ inputs.extensions }}\n        coverage: ${{ inputs.coverage }}\n      env:\n        runner: ${{ inputs.runner }}\n\n    - name: Get Composer cache directory\n      id: composer-cache\n      shell: bash\n      run: echo \"dir=$(composer config cache-files-dir)\" >> $GITHUB_OUTPUT\n\n    - name: Cache dependencies\n      uses: actions/cache@v3\n      with:\n        path: ${{ steps.composer-cache.outputs.dir }}\n        key: ${{ runner.os }}-composer-${{ inputs.php }}-${{ hashFiles('**/composer.lock') }}\n        restore-keys: ${{ runner.os }}-composer-${{ inputs.php }}-\n\n    - name: Install dependencies\n      shell: bash\n      run: composer install --prefer-dist\n"
  },
  {
    "path": ".github/actions/tag-create/action.yml",
    "content": "name: Create a repository tag\n\n#\n# Creates a tag with the given version.\n#\n# TODO: Remove once the common repo is public.\n#\n\ninputs:\n  token:\n    required: true\n  tag:\n    required: true\n\nruns:\n  using: composite\n\n  steps:\n    - shell: bash\n      run: |\n        git config user.name \"${AUTHOR_USERNAME}\"\n        git config user.email \"${AUTHOR_EMAIL}\"\n      env:\n        AUTHOR_USERNAME: ${{ github.event.pull_request.user.login }}\n        AUTHOR_EMAIL: ${{ github.event.pull_request.user.email }}\n\n    - shell: bash\n      run: |\n        git tag -a ${TAG_NAME} -m \"Version ${TAG_NAME}\"\n        git push --follow-tags\n      env:\n        TAG_NAME: ${{ inputs.tag }}\n        GITHUB_TOKEN: ${{ inputs.token }}\n"
  },
  {
    "path": ".github/actions/tag-exists/action.yml",
    "content": "name: Return a boolean indicating if a tag already exists for the repository\n\n#\n# Returns a simple true/false boolean indicating whether the tag exists or not.\n#\n# TODO: Remove once the common repo is public.\n#\n\ninputs:\n  token:\n    required: true\n  tag:\n    required: true\n\noutputs:\n  exists:\n    description: 'Whether the tag exists or not'\n    value: ${{ steps.tag-exists.outputs.EXISTS }}\n\nruns:\n  using: composite\n\n  steps:\n    - id: tag-exists\n      shell: bash\n      run: |\n        GET_API_URL=\"https://api.github.com/repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG_NAME}\"\n        http_status_code=$(curl -LI $GET_API_URL -o /dev/null -w '%{http_code}\\n' -s -H \"Authorization: token ${GITHUB_TOKEN}\")\n        if [ \"$http_status_code\" -ne \"404\" ] ; then\n          echo \"EXISTS=true\" >> $GITHUB_OUTPUT\n        else\n          echo \"EXISTS=false\" >> $GITHUB_OUTPUT\n        fi\n      env:\n        TAG_NAME: ${{ inputs.tag }}\n        GITHUB_TOKEN: ${{ inputs.token }}\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/claude-code-review.yml",
    "content": "name: Claude Code PR Review\n\non:\n  issue_comment:\n    types: [ created ]\n  pull_request_review_comment:\n    types: [ created ]\n  pull_request_review:\n    types: [ submitted ]\n\njobs:\n  claude-review:\n    permissions:\n      contents: write\n      issues: write\n      pull-requests: write\n      id-token: write\n    uses: auth0/auth0-ai-pr-analyzer-gh-action/.github/workflows/claude-code-review.yml@main"
  },
  {
    "path": ".github/workflows/matrix.json",
    "content": "{\n  \"include\": [\n    { \"php\": \"8.2\" },\n    { \"php\": \"8.3\" },\n    { \"php\": \"8.4\" }\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Create GitHub Release\n\non:\n  pull_request:\n    types:\n      - closed\n\n\n\npermissions:\n  contents: write\n  id-token: write # This is required for requesting the JWT\n\n### TODO: Replace instances of './.github/actions/' w/ `auth0/dx-sdk-actions/` and append `@latest` after the common `dx-sdk-actions` repo is made public.\n### TODO: Also remove `get-prerelease`, `get-version`, `release-create`, `tag-create` and `tag-exists` actions from this repo's .github/actions folder once the repo is public.\n\njobs:\n  rl-scanner:\n    uses: ./.github/workflows/rl-scanner.yml\n    with:\n      php-version: 8.2\n      artifact-name: 'laravel-auth0.zip'\n    secrets:\n      RLSECURE_LICENSE: ${{ secrets.RLSECURE_LICENSE }}\n      RLSECURE_SITE_KEY: ${{ secrets.RLSECURE_SITE_KEY }}\n      SIGNAL_HANDLER_TOKEN: ${{ secrets.SIGNAL_HANDLER_TOKEN }}\n      PRODSEC_TOOLS_USER: ${{ secrets.PRODSEC_TOOLS_USER }}\n      PRODSEC_TOOLS_TOKEN: ${{ secrets.PRODSEC_TOOLS_TOKEN }}\n      PRODSEC_TOOLS_ARN: ${{ secrets.PRODSEC_TOOLS_ARN }}\n\n  release:\n    if: github.event.pull_request.merged && startsWith(github.event.pull_request.head.ref, 'release/')\n    needs: rl-scanner\n    runs-on: ubuntu-latest\n\n    steps:\n      # Checkout the code\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      # Get the version from the branch name\n      - id: get_version\n        uses: ./.github/actions/get-version\n\n      # Get the prerelease flag from the branch name\n      - id: get_prerelease\n        uses: ./.github/actions/get-prerelease\n        with:\n          version: ${{ steps.get_version.outputs.version }}\n\n      # Check if the tag already exists\n      - id: tag_exists\n        uses: ./.github/actions/tag-exists\n        with:\n          tag: ${{ steps.get_version.outputs.version }}\n          token: ${{ secrets.GITHUB_TOKEN }}\n\n      # If the tag already exists, exit with an error\n      - if: steps.tag_exists.outputs.exists == 'true'\n        run: exit 1\n\n      # Publish the release to our package manager\n      - uses: ./.github/actions/publish-package\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          name: ${{ steps.get_version.outputs.version }}\n          body: ${{ github.event.pull_request.body }}\n          tag: ${{ steps.get_version.outputs.version }}\n          commit: ${{ github.sha }}\n          prerelease: ${{ steps.get_prerelease.outputs.prerelease }}\n\n      # Create a tag for the release\n      - uses: ./.github/actions/tag-create\n        with:\n          tag: ${{ steps.get_version.outputs.version }}\n          token: ${{ secrets.GITHUB_TOKEN }}\n\n      # Create a release for the tag\n      - uses: ./.github/actions/release-create\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          name: ${{ steps.get_version.outputs.version }}\n          body: ${{ github.event.pull_request.body }}\n          tag: ${{ steps.get_version.outputs.version }}\n          commit: ${{ github.sha }}\n          prerelease: ${{ steps.get_prerelease.outputs.prerelease }}\n"
  },
  {
    "path": ".github/workflows/rl-scanner.yml",
    "content": "name: RL-Secure Workflow\n\non:\n  workflow_call:\n    inputs:\n      php-version:\n        required: true\n        type: string\n      artifact-name:\n        required: true\n        type: string\n    secrets:\n      RLSECURE_LICENSE:\n        required: true\n      RLSECURE_SITE_KEY:\n        required: true\n      SIGNAL_HANDLER_TOKEN:\n        required: true\n      PRODSEC_TOOLS_USER:\n        required: true\n      PRODSEC_TOOLS_TOKEN:\n        required: true\n      PRODSEC_TOOLS_ARN:\n        required: true\n\njobs:\n  rl-scanner:\n    if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.merged && startsWith(github.event.pull_request.head.ref, 'release/'))\n    runs-on: ubuntu-latest\n    outputs:\n      scan-status: ${{ steps.rl-scan-conclusion.outcome }}\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.sha || github.ref }}\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@4bd44f22a98a19e0950cbad5f31095157cc9621b # pin@2.25.4\n        with:\n          php-version: ${{ inputs.php-version }}\n\n      - name: Build Laravel\n        shell: bash\n        run: |\n          zip -r ${{ inputs.artifact-name }} ./*\n\n      - name: Get Artifact Version\n        id: get_version\n        uses: ./.github/actions/get-version\n\n      - name: Run RL Scanner\n        id: rl-scan-conclusion\n        uses: ./.github/actions/rl-scanner\n        with:\n          artifact-path: \"$(pwd)/${{ inputs.artifact-name }}\"\n          version: \"${{ steps.get_version.outputs.version }}\"\n        env:\n          RLSECURE_LICENSE: ${{ secrets.RLSECURE_LICENSE }}\n          RLSECURE_SITE_KEY: ${{ secrets.RLSECURE_SITE_KEY }}\n          SIGNAL_HANDLER_TOKEN: ${{ secrets.SIGNAL_HANDLER_TOKEN }}\n          PRODSEC_TOOLS_USER: ${{ secrets.PRODSEC_TOOLS_USER }}\n          PRODSEC_TOOLS_TOKEN: ${{ secrets.PRODSEC_TOOLS_TOKEN }}\n          PRODSEC_TOOLS_ARN: ${{ secrets.PRODSEC_TOOLS_ARN }}\n\n      - name: Output scan result\n        run: echo \"scan-status=${{ steps.rl-scan-conclusion.outcome }}\" >> $GITHUB_ENV"
  },
  {
    "path": ".github/workflows/snyk.yml",
    "content": "name: Snyk\n\non:\n  merge_group:\n  pull_request:\n    types:\n      - opened\n      - synchronize\n  push:\n    branches:\n      - main\n  schedule:\n    - cron: \"30 0 1,15 * *\"\n\npermissions:\n  contents: read\n\nenv:\n  DX_SDKS_SNYK_ORGANIZATION: 8303ea71-ac72-4ae6-9cd0-ae2f3eda82b7\n  DX_SDKS_SNYK_PROJECT: auth0/laravel-auth0\n  DX_SDKS_SNYK_TAGS: Refactoring-target:DX,Refactoring-origin:auth0-sdks\n  DX_SDKS_SNYK_REMOTE_REPO_URL: https://github.com/auth0/laravel-auth0\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}\n\njobs:\n  configure:\n    name: Configure\n    runs-on: ubuntu-latest\n\n    outputs:\n      matrix: ${{ steps.set-matrix.outputs.matrix }}\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          ref: ${{ github.event.pull_request.merge_commit_sha || github.ref }}\n\n      - id: set-matrix\n        run: echo \"matrix=$(jq -c . < ./.github/workflows/matrix.json)\" >> $GITHUB_OUTPUT\n\n  check:\n    needs: [configure]\n\n    name: Check for Vulnerabilities\n    runs-on: ubuntu-latest\n\n    steps:\n      - if: github.actor == 'dependabot[bot]' || github.event_name == 'merge_group'\n        run: exit 0\n\n      - uses: actions/checkout@v4\n        with:\n          ref: ${{ github.event.pull_request.merge_commit_sha || github.ref }}\n\n      - uses: ./.github/actions/setup\n        with:\n          php: ${{ fromJson(needs.configure.outputs.matrix).include[0].php }}\n\n      - run: npm install snyk -g\n\n      - if: github.ref == 'refs/heads/main'\n        run: snyk monitor --file=composer.lock --org=$SNYK_ORGANIZATION --project-name=$SNYK_PROJECT --project-tags=$SNYK_TAGS --remote-repo-url=$SNYK_REMOTE_REPO --target-reference=\"$(git branch --show-current)\"\n        env:\n          SNYK_TOKEN: ${{ secrets.DX_SDKS_SNYK_TOKEN }}\n          SNYK_ORGANIZATION: ${{ env.DX_SDKS_SNYK_ORGANIZATION }}\n          SNYK_PROJECT: ${{ env.DX_SDKS_SNYK_PROJECT }}\n          SNYK_TAGS: ${{ env.DX_SDKS_SNYK_TAGS }}\n          SNYK_REMOTE_REPO: ${{ env.DX_SDKS_SNYK_REMOTE_REPO_URL }}\n        continue-on-error: true\n\n      - run: snyk test --file=composer.lock --org=$SNYK_ORGANIZATION --project-name=$SNYK_PROJECT --remote-repo-url=$SNYK_REMOTE_REPO\n        env:\n          SNYK_TOKEN: ${{ secrets.DX_SDKS_SNYK_TOKEN }}\n          SNYK_ORGANIZATION: ${{ env.DX_SDKS_SNYK_ORGANIZATION }}\n          SNYK_PROJECT: ${{ env.DX_SDKS_SNYK_PROJECT }}\n          SNYK_TAGS: ${{ env.DX_SDKS_SNYK_TAGS }}\n          SNYK_REMOTE_REPO: ${{ env.DX_SDKS_SNYK_REMOTE_REPO_URL }}\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Build and Test\n\non:\n  merge_group:\n  workflow_dispatch:\n  pull_request:\n    types:\n      - opened\n      - synchronize\n  push:\n    branches:\n      - main\n\npermissions: {}\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}\n\njobs:\n  configure:\n    name: Configure\n    runs-on: ubuntu-latest\n\n    outputs:\n      matrix: ${{ steps.set-matrix.outputs.matrix }}\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.ref }}\n\n      - id: set-matrix\n        run: echo \"matrix=$(jq -c . < ./.github/workflows/matrix.json)\" >> $GITHUB_OUTPUT\n\n  prepare:\n    name: Prepare Dependencies\n    needs: [configure]\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.configure.outputs.matrix) }}\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.ref }}\n\n      - uses: ./.github/actions/setup\n        with:\n          php: ${{ matrix.php }}\n\n  composer-normalize:\n    name: Composer Normalize\n    needs: [configure, prepare]\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.configure.outputs.matrix) }}\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.ref }}\n\n      - uses: ./.github/actions/setup\n        with:\n          php: ${{ matrix.php }}\n\n      - run: composer normalize --dry-run --diff\n\n  composer-validate:\n    name: Composer Validate\n    needs: [configure, prepare]\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.configure.outputs.matrix) }}\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.ref }}\n\n      - uses: ./.github/actions/setup\n        with:\n          php: ${{ matrix.php }}\n\n      - run: composer validate\n\n  pest:\n    name: PEST\n    needs: [configure, prepare]\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.configure.outputs.matrix) }}\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.ref }}\n\n      - uses: ./.github/actions/setup\n        with:\n          php: ${{ matrix.php }}\n          coverage: pcov\n\n      - if: matrix.php == '8.2'\n        run: composer pest:coverage\n\n      - if: matrix.php == '8.2'\n        uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # pin@3.1.4\n        with:\n          directory: ./coverage/\n          flags: unittestsvalidate\n\n  phpstan:\n    name: PHPStan\n    needs: [configure, prepare]\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.configure.outputs.matrix) }}\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.ref }}\n\n      - uses: ./.github/actions/setup\n        with:\n          php: ${{ matrix.php }}\n\n      - run: composer phpstan\n\n  psalm:\n    name: Psalm\n    needs: [configure, prepare]\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.configure.outputs.matrix) }}\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.ref }}\n\n      - uses: ./.github/actions/setup\n        with:\n          php: ${{ matrix.php }}\n\n      - run: composer psalm\n\n  rector:\n    name: Rector\n    needs: [configure, prepare]\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.configure.outputs.matrix) }}\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.ref }}\n\n      - uses: ./.github/actions/setup\n        with:\n          php: ${{ matrix.php }}\n\n      - run: composer rector\n\n  php-cs-fixer:\n    name: PHP CS Fixer\n    needs: [configure, prepare]\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.configure.outputs.matrix) }}\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha || github.ref }}\n\n      - uses: ./.github/actions/setup\n        with:\n          php: ${{ matrix.php }}\n\n      - run: composer phpcs\n"
  },
  {
    "path": ".gitignore",
    "content": "build/\nvendor/\ncoverage/\ntmp/\n.idea/\n.env\n.DS_Store\ncomposer.lock\ncomposer.phar\n.phpunit.result.cache\ncomposer.local.json\ncomposer.local.json_\n.php-cs-fixer.cache\ncomposer.local.old\n.vscode\npest.log\nNOTES.md\n\n# AI tools\n.claude\n"
  },
  {
    "path": ".php-cs-fixer.dist.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nreturn (new PhpCsFixer\\Config())\n    ->setRiskyAllowed(true)\n    ->setRules([\n        'array_indentation' => true,\n        'array_push' => true,\n        'array_syntax' => ['syntax' => 'short'],\n        'assign_null_coalescing_to_coalesce_equal' => true,\n        'backtick_to_shell_exec' => true,\n        'binary_operator_spaces' => true,\n        'blank_line_after_namespace' => true,\n        'blank_line_after_opening_tag' => true,\n        'blank_line_before_statement' => true,\n        'blank_line_between_import_groups' => true,\n        'braces' => true,\n        'cast_spaces' => true,\n        'class_attributes_separation' => ['elements' => ['const' => 'one', 'method' => 'one', 'property' => 'one', 'trait_import' => 'one', 'case' => 'one']],\n        'class_definition' => ['multi_line_extends_each_single_line' => true, 'single_line' => true, 'single_item_single_line' => true, 'space_before_parenthesis' => false, 'inline_constructor_arguments' => false],\n        'class_reference_name_casing' => true,\n        'clean_namespace' => true,\n        'combine_consecutive_issets' => true,\n        'combine_consecutive_unsets' => true,\n        'combine_nested_dirname' => true,\n        'comment_to_phpdoc' => ['ignored_tags' => ['codeCoverageIgnoreStart', 'codeCoverageIgnoreEnd', 'phpstan-ignore-next-line']],\n        'compact_nullable_typehint' => true,\n        'concat_space' => ['spacing' => 'one'],\n        'constant_case' => ['case' => 'lower'],\n        'curly_braces_position' => ['control_structures_opening_brace' => 'same_line', 'functions_opening_brace' => 'next_line_unless_newline_at_signature_end', 'anonymous_functions_opening_brace' => 'same_line', 'classes_opening_brace' => 'next_line_unless_newline_at_signature_end', 'anonymous_classes_opening_brace' => 'same_line', 'allow_single_line_empty_anonymous_classes' => true, 'allow_single_line_anonymous_functions' => true],\n        'date_time_create_from_format_call' => true,\n        'date_time_immutable' => true,\n        'declare_equal_normalize' => ['space' => 'none'],\n        'declare_parentheses' => true,\n        'declare_strict_types' => true,\n        'dir_constant' => true,\n        'doctrine_annotation_array_assignment' => true,\n        'doctrine_annotation_braces' => true,\n        'doctrine_annotation_indentation' => true,\n        'doctrine_annotation_spaces' => true,\n        'echo_tag_syntax' => ['format' => 'long'],\n        'elseif' => true,\n        'empty_loop_body' => true,\n        'empty_loop_condition' => true,\n        'encoding' => true,\n        'ereg_to_preg' => true,\n        'error_suppression' => true,\n        'escape_implicit_backslashes' => true,\n        'explicit_indirect_variable' => true,\n        'explicit_string_variable' => true,\n        'final_class' => true,\n        'final_internal_class' => true,\n        'final_public_method_for_abstract_class' => true,\n        'fopen_flag_order' => true,\n        'fopen_flags' => true,\n        'full_opening_tag' => true,\n        'fully_qualified_strict_types' => true,\n        'function_declaration' => true,\n        'function_to_constant' => true,\n        'function_typehint_space' => true,\n        'general_phpdoc_annotation_remove' => true,\n        'general_phpdoc_tag_rename' => true,\n        'get_class_to_class_keyword' => true,\n        'global_namespace_import' => ['import_classes' => true, 'import_constants' => true, 'import_functions' => true],\n        'group_import' => true,\n        'heredoc_indentation' => true,\n        'heredoc_to_nowdoc' => true,\n        'implode_call' => true,\n        'include' => true,\n        'increment_style' => ['style' => 'pre'],\n        'indentation_type' => true,\n        'integer_literal_case' => true,\n        'is_null' => true,\n        'lambda_not_used_import' => true,\n        'line_ending' => true,\n        'linebreak_after_opening_tag' => true,\n        'list_syntax' => ['syntax' => 'short'],\n        'logical_operators' => true,\n        'lowercase_cast' => true,\n        'lowercase_keywords' => true,\n        'lowercase_static_reference' => true,\n        'magic_constant_casing' => true,\n        'magic_method_casing' => true,\n        'mb_str_functions' => false,\n        'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline', 'after_heredoc' => true],\n        'method_chaining_indentation' => true,\n        'modernize_strpos' => true,\n        'modernize_types_casting' => true,\n        'multiline_comment_opening_closing' => true,\n        'multiline_whitespace_before_semicolons' => true,\n        'native_function_casing' => true,\n        'native_function_invocation' => true,\n        'native_function_type_declaration_casing' => true,\n        'new_with_braces' => true,\n        'no_alias_functions' => true,\n        'no_alias_language_construct_call' => true,\n        'no_alternative_syntax' => true,\n        'no_binary_string' => true,\n        'no_blank_lines_after_class_opening' => true,\n        'no_blank_lines_after_phpdoc' => true,\n        'no_break_comment' => true,\n        'no_closing_tag' => true,\n        'no_empty_comment' => true,\n        'no_empty_phpdoc' => true,\n        'no_empty_statement' => true,\n        'no_extra_blank_lines' => true,\n        'no_homoglyph_names' => true,\n        'no_leading_import_slash' => true,\n        'no_leading_namespace_whitespace' => true,\n        'no_mixed_echo_print' => true,\n        'no_multiline_whitespace_around_double_arrow' => true,\n        'no_multiple_statements_per_line' => true,\n        'no_php4_constructor' => true,\n        'no_short_bool_cast' => true,\n        'no_singleline_whitespace_before_semicolons' => true,\n        'no_space_around_double_colon' => true,\n        'no_spaces_after_function_name' => true,\n        'no_spaces_around_offset' => true,\n        'no_spaces_inside_parenthesis' => true,\n        'no_superfluous_elseif' => true,\n        'no_trailing_comma_in_singleline' => true,\n        'no_trailing_whitespace_in_comment' => true,\n        'no_trailing_whitespace_in_string' => true,\n        'no_trailing_whitespace' => true,\n        'no_unneeded_control_parentheses' => true,\n        'no_unneeded_curly_braces' => true,\n        'no_unneeded_final_method' => true,\n        'no_unneeded_import_alias' => true,\n        'no_unreachable_default_argument_value' => true,\n        'no_unset_cast' => true,\n        'no_unused_imports' => true,\n        'no_useless_concat_operator' => true,\n        'no_useless_else' => true,\n        'no_useless_nullsafe_operator' => true,\n        'no_useless_return' => true,\n        'no_useless_sprintf' => true,\n        'no_whitespace_before_comma_in_array' => true,\n        'no_whitespace_in_blank_line' => true,\n        'non_printable_character' => true,\n        'normalize_index_brace' => true,\n        'not_operator_with_successor_space' => true,\n        'nullable_type_declaration_for_default_null_value' => true,\n        'object_operator_without_whitespace' => true,\n        'octal_notation' => true,\n        'operator_linebreak' => true,\n        'ordered_class_elements' => ['sort_algorithm' => 'alpha', 'order' => ['use_trait', 'case', 'constant', 'constant_private', 'constant_protected', 'constant_public', 'property_private', 'property_private_readonly', 'property_private_static', 'property_protected', 'property_protected_readonly', 'property_protected_static', 'property_public', 'property_public_readonly', 'property_public_static', 'property_static', 'protected', 'construct', 'destruct', 'magic', 'method', 'public', 'method_public', 'method_abstract', 'method_public_abstract', 'method_public_abstract_static', 'method_public_static', 'method_static', 'method_private', 'method_private_abstract', 'method_private_abstract_static', 'method_private_static', 'method_protected', 'method_protected_abstract', 'method_protected_abstract_static', 'method_protected_static', 'phpunit', 'private', 'property']],\n        'ordered_imports' => ['sort_algorithm' => 'alpha', 'imports_order' => ['const', 'class', 'function']],\n        'ordered_interfaces' => true,\n        'ordered_traits' => true,\n        'php_unit_fqcn_annotation' => true,\n        'phpdoc_add_missing_param_annotation' => ['only_untyped' => false],\n        'phpdoc_align' => ['align' => 'vertical'],\n        'phpdoc_indent' => true,\n        'phpdoc_inline_tag_normalizer' => true,\n        'phpdoc_line_span' => true,\n        'phpdoc_no_access' => true,\n        'phpdoc_no_empty_return' => true,\n        'phpdoc_no_package' => true,\n        'phpdoc_no_useless_inheritdoc' => true,\n        'phpdoc_order_by_value' => true,\n        'phpdoc_order' => true,\n        'phpdoc_return_self_reference' => ['replacements' => ['this' => 'self']],\n        'phpdoc_scalar' => true,\n        'phpdoc_separation' => true,\n        'phpdoc_single_line_var_spacing' => true,\n        'phpdoc_summary' => true,\n        'phpdoc_tag_type' => true,\n        'phpdoc_to_comment' => ['ignored_tags' => ['var']],\n        'phpdoc_trim_consecutive_blank_line_separation' => true,\n        'phpdoc_trim' => true,\n        'phpdoc_types_order' => true,\n        'phpdoc_types' => true,\n        'phpdoc_var_annotation_correct_order' => true,\n        'phpdoc_var_without_name' => true,\n        'pow_to_exponentiation' => true,\n        'protected_to_private' => true,\n        'psr_autoloading' => true,\n        'random_api_migration' => true,\n        'regular_callable_call' => true,\n        'return_assignment' => true,\n        'return_type_declaration' => ['space_before' => 'none'],\n        'return_type_declaration' => true,\n        'self_accessor' => true,\n        'self_static_accessor' => true,\n        'semicolon_after_instruction' => true,\n        'set_type_to_cast' => true,\n        'short_scalar_cast' => true,\n        'simple_to_complex_string_variable' => true,\n        'simplified_if_return' => true,\n        'single_blank_line_at_eof' => true,\n        'single_blank_line_before_namespace' => true,\n        'single_class_element_per_statement' => true,\n        'single_line_after_imports' => true,\n        'single_line_comment_spacing' => true,\n        'single_line_comment_style' => ['comment_types' => ['hash']],\n        'single_line_throw' => true,\n        'single_quote' => true,\n        'single_space_after_construct' => true,\n        'single_space_around_construct' => true,\n        'single_trait_insert_per_statement' => true,\n        'space_after_semicolon' => true,\n        'standardize_increment' => true,\n        'standardize_not_equals' => true,\n        'statement_indentation' => true,\n        'static_lambda' => false,\n        'strict_comparison' => true,\n        'strict_param' => true,\n        'string_length_to_empty' => true,\n        'string_line_ending' => true,\n        'switch_case_semicolon_to_colon' => true,\n        'switch_case_space' => true,\n        'switch_continue_to_break' => true,\n        'ternary_operator_spaces' => true,\n        'ternary_to_elvis_operator' => true,\n        'ternary_to_null_coalescing' => true,\n        'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['arguments', 'arrays', 'match', 'parameters']],\n        'trim_array_spaces' => true,\n        'types_spaces' => ['space' => 'single', 'space_multiple_catch' => 'single'],\n        'unary_operator_spaces' => true,\n        'use_arrow_functions' => true,\n        'visibility_required' => true,\n        'void_return' => true,\n        'whitespace_after_comma_in_array' => true,\n        'yoda_style' => true,\n    ])\n    ->setFinder(\n        PhpCsFixer\\Finder::create()\n            ->exclude('vendor')\n            ->in([__DIR__ . '/src/', __DIR__ . '/deprecated/']),\n    );\n"
  },
  {
    "path": ".phpcs.xml.dist",
    "content": "<?xml version=\"1.0\"?>\n<ruleset>\n    <rule ref=\"Squiz.Commenting.FunctionComment\">\n        <properties>\n            <property name=\"minimumVisibility\" value=\"public\" />\n            <property name=\"skipIfInheritdoc\" value=\"true\" />\n        </properties>\n    </rule>\n\n\t<rule ref=\"Generic.PHP.Syntax\">\n\t\t<exclude name=\"Generic.PHP.Syntax\" />\n\t</rule>\n</ruleset>\n"
  },
  {
    "path": ".semgrepignore",
    "content": ".github/\ndocs/\nexamples/\ntests/\n\\*.md\n"
  },
  {
    "path": ".shiprc",
    "content": "{\n  \"files\": {\n    \"src/ServiceAbstract.php\": [],\n    \".version\": []\n  },\n  \"prefixVersion\": false\n}\n"
  },
  {
    "path": ".version",
    "content": "7.22.0\n"
  },
  {
    "path": "CHANGELOG.ARCHIVE.md",
    "content": "# Changelog Archive\n\nThis file contains changes for all versions of this package prior to the latest major, 7.0.\n\nThe changelog for the latest changes is [CHANGELOG.md](./CHANGELOG.md).\n\n## [6.5.0](https://github.com/auth0/laravel-auth0/tree/6.5.0) (2021-10-15)\n\n### Added\n\n-   Add SDK alias methods for passwordless endpoints [\\#228](https://github.com/auth0/laravel-auth0/pull/228)\n\n## [6.4.1](https://github.com/auth0/laravel-auth0/tree/6.4.0) (2021-08-02)\n\n### Fixed\n\n-   Use the fully qualified facade class names [\\#215](https://github.com/auth0/laravel-auth0/pull/215)\n-   Update auth0-PHP dependency [\\#222](https://github.com/auth0/laravel-auth0/pull/222)\n-   Pass api_identifier config as audience to Auth0\\SDK\\Auth0 [\\#214](https://github.com/auth0/laravel-auth0/pull/214)\n\n## [6.4.0](https://github.com/auth0/laravel-auth0/tree/6.4.0) (2021-03-25)\n\n### Improved\n\n-   Add support for Auth0 Organizations [\\#209](https://github.com/auth0/laravel-auth0/pull/209)\n\n## [6.3.0](https://github.com/auth0/laravel-auth0/tree/6.3.0) (2020-02-18)\n\n### Improved\n\n-   Store changes made to the user object during the onLogin event hook [\\#206](https://github.com/auth0/laravel-auth0/pull/206)\n\n### Fixed\n\n-   Avoid throwing an error when calling getUserByUserInfo() during login callback event when the supplied profile is empty/null [\\#207](https://github.com/auth0/laravel-auth0/pull/207)\n\n## [6.2.0](https://github.com/auth0/laravel-auth0/tree/6.2.0) (2020-01-15)\n\n### Added\n\n-   Support PHP 8.0 [\\#200](https://github.com/auth0/laravel-auth0/pull/200)\n\n### Fixed\n\n-   Fix the missing `return null;` in `getUserByIdentifier` [\\#201](https://github.com/auth0/laravel-auth0/pull/201)\n\n## [6.1.0](https://github.com/auth0/laravel-auth0/tree/6.1.0) (2020-09-17)\n\n### Added\n\n-   Support Laravel 8 [\\#190](https://github.com/auth0/laravel-auth0/pull/190)\n\n### Fixed\n\n-   Fix composer.json whitespace issue [\\#192](https://github.com/auth0/laravel-auth0/pull/192)\n\n## [6.0.1](https://github.com/auth0/laravel-auth0/tree/6.0.1) (2020-04-28)\n\n### Fixed\n\n-   Fix access token decoding and validation [\\#183](https://github.com/auth0/laravel-auth0/pull/183)\n\n## [6.0.0](https://github.com/auth0/laravel-auth0/tree/6.0.0) (2020-04-09)\n\n**This is a major release and includes breaking changes!** This release also includes a major version change for the PHP SDK that it relies on. Please see the [migration guide](https://github.com/auth0/auth0-PHP/blob/master/MIGRATE-v5-TO-v7.md) for the PHP SDK for more information.\n\n### Added\n\n-   auth0-PHP 7.0 - State and nonce handling [\\#163](https://github.com/auth0/laravel-auth0/issues/163)\n-   Implement auth0 guard [\\#166](https://github.com/auth0/laravel-auth0/pull/166)\n\n### Improved\n\n-   Use array for Auth0JWTUser and add repo return types [\\#176](https://github.com/auth0/laravel-auth0/pull/176)\n-   Update PHP SDK to v7.0.0 [\\#162](https://github.com/auth0/laravel-auth0/pull/162)\n-   Bind SessionState handler interface in container [\\#147](https://github.com/auth0/laravel-auth0/pull/147)\n\n### Fixed\n\n-   Fix Laravel session management [\\#174](https://github.com/auth0/laravel-auth0/pull/174)\n-   Cannot use actingAs unit tests functionality [\\#161](https://github.com/auth0/laravel-auth0/issues/161)\n\n## [5.4.0](https://github.com/auth0/laravel-auth0/tree/5.4.0) (2020-03-27)\n\n### Added\n\n-   Laravel 7 support [\\#167](https://github.com/auth0/laravel-auth0/pull/167)\n\n### Fixed\n\n-   Laravel 7.0 supported release. [\\#171](https://github.com/auth0/laravel-auth0/issues/171)\n-   Fixed PHPDocs [\\#170](https://github.com/auth0/laravel-auth0/pull/170)\n\n## [5.3.1](https://github.com/auth0/laravel-auth0/tree/5.3.1) (2019-11-14)\n\n### Fixed\n\n-   Setting of state_handler in Auth0Service causes \"Invalid state\" error [\\#154](https://github.com/auth0/laravel-auth0/issues/154)\n-   Allow store and state_handler to be passed in from config [\\#156](https://github.com/auth0/laravel-auth0/pull/156)\n-   Add 'persist_refresh_token' key to laravel-auth0 configuration file. [\\#152](https://github.com/auth0/laravel-auth0/pull/152)\n-   Replace `setEnvironment` with `setEnvProperty` [\\#145](https://github.com/auth0/laravel-auth0/pull/145)\n\n## [5.3.0](https://github.com/auth0/laravel-auth0/tree/5.3.0) (2019-09-26)\n\n### Added\n\n-   Support Laravel 6 [\\#139](https://github.com/auth0/laravel-auth0/pull/139)\n-   Feature request: Add Laravel 6 support [\\#138](https://github.com/auth0/laravel-auth0/issues/138)\n\n### Fixed\n\n-   Use LaravelSessionStore in the SessionStateHandler. [\\#135](https://github.com/auth0/laravel-auth0/pull/135)\n-   SessionStateHandler should use LaravelSessionStore not SessionStore [\\#125](https://github.com/auth0/laravel-auth0/issues/125)\n\n## [5.2.0](https://github.com/auth0/laravel-auth0/tree/5.2.0) (2019-06-27)\n\n### Added\n\n-   Authenticate as a Laravel API user using the Auth0 token [\\#129](https://github.com/auth0/laravel-auth0/issues/129)\n-   Redirect to previous page after login [\\#122](https://github.com/auth0/laravel-auth0/issues/122)\n-   Auth0User uses private variables so they cannot be accessed or overridden in child class [\\#120](https://github.com/auth0/laravel-auth0/issues/120)\n-   API routes broken in auth0-laravel-php-web-app (and in general)? [\\#117](https://github.com/auth0/laravel-auth0/issues/117)\n-   API returning \"token algorithm not supported\" [\\#116](https://github.com/auth0/laravel-auth0/issues/116)\n-   Changing name of user identifier [\\#115](https://github.com/auth0/laravel-auth0/issues/115)\n-   Possible to use User object functions? [\\#114](https://github.com/auth0/laravel-auth0/issues/114)\n-   Auth0-PHP@5.3.1 breaks Laravel-Auth0 [\\#108](https://github.com/auth0/laravel-auth0/issues/108)\n-   Extend Illuminate\\Foundation\\Auth\\User [\\#104](https://github.com/auth0/laravel-auth0/issues/104)\n-   [Bug] Inconsistencies with the singleton Auth0Service [\\#103](https://github.com/auth0/laravel-auth0/issues/103)\n-   How do you combine Auth0 Lock with Laravel Auth0? [\\#102](https://github.com/auth0/laravel-auth0/issues/102)\n-   OnLogin callback question [\\#97](https://github.com/auth0/laravel-auth0/issues/97)\n-   Add composer.lock file [\\#123](https://github.com/auth0/laravel-auth0/pull/123) ([lbalmaceda](https://github.com/lbalmaceda))\n\n### Improved\n\n-   Change private properties to protected [\\#132](https://github.com/auth0/laravel-auth0/pull/132)\n-   Return null instead of false in Auth0UserProvider. [\\#128](https://github.com/auth0/laravel-auth0/pull/128)\n-   Change the visibility of the getter method from private to public [\\#121](https://github.com/auth0/laravel-auth0/pull/121)\n-   Updated required PHP version to 5.4 in composer [\\#118](https://github.com/auth0/laravel-auth0/pull/118)\n-   Changed arrays to use short array syntax [\\#110](https://github.com/auth0/laravel-auth0/pull/110)\n\n### Fixed\n\n-   Fix cachehandler resolving issues [\\#131](https://github.com/auth0/laravel-auth0/pull/131)\n-   Added the Auth0Service as a singleton through the classname [\\#107](https://github.com/auth0/laravel-auth0/pull/107)\n-   Fixed typo [\\#106](https://github.com/auth0/laravel-auth0/pull/106)\n\n## [5.1.0](https://github.com/auth0/laravel-auth0/tree/5.1.0) (2018-03-20)\n\n### Added\n\n-   AutoDiscovery [\\#91](https://github.com/auth0/laravel-auth0/pull/91) ([m1guelpf](https://github.com/m1guelpf))\n-   Added guzzle options to config to allow for connection options [\\#88](https://github.com/auth0/laravel-auth0/pull/88)\n\n### Improved\n\n-   Change default settings file [\\#96](https://github.com/auth0/laravel-auth0/pull/96)\n-   Utilise Auth0->Login to ensure state validation [\\#90](https://github.com/auth0/laravel-auth0/pull/90)\n\n### Fixed\n\n-   Make code comments gender neutral [\\#98](https://github.com/auth0/laravel-auth0/pull/98)\n-   Fix README and CHANGELOG [\\#99](https://github.com/auth0/laravel-auth0/pull/99)\n-   pls change config arg name [\\#95](https://github.com/auth0/laravel-auth0/issues/95)\n\n## [5.0.2](https://github.com/auth0/laravel-auth0/tree/5.0.2) (2017-08-30)\n\n### Fixed\n\n-   Use instead of to identify the Auth0 user [\\#80](https://github.com/auth0/laravel-auth0/pull/80)\n\n## [5.0.1](https://github.com/auth0/laravel-auth0/tree/5.0.1) (2017-02-23)\n\n### Fixed\n\n-   Fixed `supported_algs` configuration name\n\n## [5.0.0](https://github.com/auth0/laravel-auth0/tree/5.0.0) (2017-02-22)\n\n### Fixed\n\n-   V5: update to auth0 sdk v5 [\\#69](https://github.com/auth0/laravel-auth0/pull/69)\n\n## [4.0.8](https://github.com/auth0/laravel-auth0/tree/4.0.8) (2017-01-27)\n\n### Fixed\n\n-   Allow use of RS256 Protocol [\\#63](https://github.com/auth0/wp-auth0/issues/63)\n-   Add RS256 to the list of supported algorithms [\\#62](https://github.com/auth0/wp-auth0/issues/62)\n-   allow to configure the algorithm supported for token verification [\\#65](https://github.com/auth0/laravel-auth0/pull/65)\n\n## [4.0.7](https://github.com/auth0/laravel-auth0/tree/4.0.7) (2017-01-02)\n\n### Fixed\n\n-   it should pass all the configs to the oauth client [\\#64](https://github.com/auth0/laravel-auth0/pull/64)\n\n## [4.0.6](https://github.com/auth0/laravel-auth0/tree/4.0.6) (2016-11-29)\n\n### Fixed\n\n-   Code style & docblocks [\\#56](https://github.com/auth0/laravel-auth0/pull/56)\n-   Adding accessor to retrieve JWT from Auth0Service [\\#58](https://github.com/auth0/laravel-auth0/pull/58)\n\n## [4.0.5](https://github.com/auth0/laravel-auth0/tree/4.0.5) (2016-11-29)\n\n### Fixed\n\n-   Added flag for not encoded tokens + removed example [\\#57](https://github.com/auth0/laravel-auth0/pull/57)\n\n## [4.0.4](https://github.com/auth0/laravel-auth0/tree/4.0.4) (2016-11-25)\n\n### Fixed\n\n-   Fixing config type [\\#55](https://github.com/auth0/laravel-auth0/pull/55)\n\n## [4.0.2](https://github.com/auth0/laravel-auth0/tree/4.0.2) (2016-10-03)\n\n### Fixed\n\n-   Fixing JWTVerifier [\\#54](https://github.com/auth0/laravel-auth0/pull/54)\n\n## [4.0.1](https://github.com/auth0/laravel-auth0/tree/4.0.1) (2016-09-19)\n\n### Fixed\n\n-   Fix error becuase of contract and class with the same name [\\#52](https://github.com/auth0/laravel-auth0/pull/52)\n\n## [4.0.0](https://github.com/auth0/laravel-auth0/tree/4.0.0) (2016-09-15)\n\n### Improved\n\n-   Better support for Laravel 5.3: Support for Laravel Passport for token verification\n    Support of auth0 PHP sdk v4 with JWKs cache\n\n### Fixed\n\n-   Merge pull request #50 from auth0/4.x.x-dev [\\#50](https://github.com/auth0/laravel-auth0/pull/50)\n\n## [3.2.1](https://github.com/auth0/laravel-auth0/tree/3.2.1) (2016-09-12)\n\n### Fixed\n\n-   Fix for Laravel 5.2 [\\#49](https://github.com/auth0/laravel-auth0/pull/49)\n\n## [3.2.0](https://github.com/auth0/laravel-auth0/tree/3.2.0) (2016-07-11)\n\n### Fixed\n\n-   New optional jwt middleware [\\#40](https://github.com/auth0/laravel-auth0/pull/40)\n\n## [3.1.0](https://github.com/auth0/laravel-auth0/tree/3.1.0) (2016-05-02)\n\n### Fixed\n\n-   3.1.0 [\\#36](https://github.com/auth0/laravel-auth0/pull/36)\n\n## [3.0.3](https://github.com/auth0/laravel-auth0/tree/3.0.3) (2016-01-28)\n\n### Fixed\n\n-   Tag 2.2.2 breaks on Laravel 5.1 [\\#30](https://github.com/auth0/laravel-auth0/issues/30)\n-   Conform to 5.2's Authenticatable contract [\\#31](https://github.com/auth0/laravel-auth0/pull/31)\n\n## [3.0.2](https://github.com/auth0/laravel-auth0/tree/3.0.2) (2016-01-25)\n\n### Fixed\n\n-   Added optional persistence configuration values [\\#29](https://github.com/auth0/laravel-auth0/pull/29)\n\n## [2.2.1](https://github.com/auth0/laravel-auth0/tree/2.2.1) (2016-01-22)\n\n### Fixed\n\n-   Create a logout route [\\#25](https://github.com/auth0/laravel-auth0/issues/25)\n-   Auth0 SDK checks for null values instead of false [\\#27](https://github.com/auth0/laravel-auth0/pull/27)\n\n## [3.0.1](https://github.com/auth0/laravel-auth0/tree/3.0.1) (2016-01-18)\n\n### Fixed\n\n-   updated auth0-php dependency [\\#24](https://github.com/auth0/laravel-auth0/pull/24)\n\n## [3.0.0](https://github.com/auth0/laravel-auth0/tree/3.0.0) (2016-01-06)\n\n### Fixed\n\n-   auth0/auth0-php ~1.0 requirement doesn't support latest GuzzleHttp [\\#21](https://github.com/auth0/laravel-auth0/issues/21)\n-   updated to be compatible with laravel 5.2 [\\#23](https://github.com/auth0/laravel-auth0/pull/23)\n\n## [2.2.0](https://github.com/auth0/laravel-auth0/tree/2.2.0) (2015-11-30)\n\n### Fixed\n\n-   updated auth0-php dependency version [\\#22](https://github.com/auth0/laravel-auth0/pull/22)\n-   Update login.blade.php [\\#20](https://github.com/auth0/laravel-auth0/pull/20)\n\n## [2.1.4](https://github.com/auth0/laravel-auth0/tree/2.1.4) (2015-10-27)\n\n### Fixed\n\n-   Middleware contract has been deprecated in 5.1 [\\#19](https://github.com/auth0/laravel-auth0/pull/19)\n-   Fixed some typo's in the comments. [\\#18](https://github.com/auth0/laravel-auth0/pull/18)\n-   Removed note about unstable dependency from README [\\#17](https://github.com/auth0/laravel-auth0/pull/17)\n-   Update composer instructions [\\#16](https://github.com/auth0/laravel-auth0/pull/16)\n-   Use a tagged release of adoy/oauth2 [\\#15](https://github.com/auth0/laravel-auth0/pull/15)\n\n## [2.1.3](https://github.com/auth0/laravel-auth0/tree/2.1.3) (2015-07-17)\n\n### Fixed\n\n-   updated jwt dependency [\\#14](https://github.com/auth0/laravel-auth0/pull/14)\n\n## [2.1.2](https://github.com/auth0/laravel-auth0/tree/2.1.2) (2015-05-15)\n\n### Fixed\n\n-   Added override of info headers [\\#13](https://github.com/auth0/laravel-auth0/pull/13)\n\n## [2.1.1](https://github.com/auth0/laravel-auth0/tree/2.1.1) (2015-05-12)\n\n### Fixed\n\n-   SDK Client headers spec compliant [\\#11](https://github.com/auth0/laravel-auth0/issues/11)\n-   Support for Laravel 5? [\\#6](https://github.com/auth0/laravel-auth0/issues/6)\n-   SDK Client headers spec compliant \\#11 [\\#12](https://github.com/auth0/laravel-auth0/pull/12)\n\n## [2.1.0](https://github.com/auth0/laravel-auth0/tree/2.1.0) (2015-05-07)\n\n### Fixed\n\n-   Upgrade to auth-php 1.0.0: Added support to API V2 [\\#10](https://github.com/auth0/laravel-auth0/pull/10)\n\n## [2.0.0](https://github.com/auth0/laravel-auth0/tree/2.0.0) (2015-04-20)\n\n### Fixed\n\n-   Package V2 for Laravel5 [\\#9](https://github.com/auth0/laravel-auth0/pull/9)\n\n## [1.0.8](https://github.com/auth0/laravel-auth0/tree/1.0.8) (2015-04-14)\n\n-   [Full Changelog](https://github.com/auth0/laravel-auth0/compare/1.0.7...1.0.8)\n\n## [1.0.7](https://github.com/auth0/laravel-auth0/tree/1.0.7) (2015-04-13)\n\n### Fixed\n\n-   Fixed the way the access token is pased to the A0User [\\#7](https://github.com/auth0/laravel-auth0/pull/7)\n-   Update README.md [\\#5](https://github.com/auth0/laravel-auth0/pull/5)\n\n## [1.0.6](https://github.com/auth0/laravel-auth0/tree/1.0.6) (2014-08-01)\n\n-   [Full Changelog](https://github.com/auth0/laravel-auth0/compare/1.0.5...1.0.6)\n\n## [1.0.5](https://github.com/auth0/laravel-auth0/tree/1.0.5) (2014-08-01)\n\n### Fixed\n\n-   Problem with normal laravel user table [\\#4](https://github.com/auth0/laravel-auth0/issues/4)\n-   Update README.md [\\#3](https://github.com/auth0/laravel-auth0/pull/3)\n\n## [1.0.4](https://github.com/auth0/laravel-auth0/tree/1.0.4) (2014-05-07)\n\n-   [Full Changelog](https://github.com/auth0/laravel-auth0/compare/1.0.3...1.0.4)\n\n## [1.0.3](https://github.com/auth0/laravel-auth0/tree/1.0.3) (2014-04-21)\n\n-   [Full Changelog](https://github.com/auth0/laravel-auth0/compare/1.0.0...1.0.3)\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\n## [7.22.0](https://github.com/auth0/laravel-auth0/tree/7.22.0) (2026-04-08)\n\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.21.0...7.22.0)\n\n**Added**\n\n- Laravel 13 support [\\#491](https://github.com/auth0/laravel-auth0/pull/491) ([cosmastech](https://github.com/cosmastech))\n\n**Fixed**\n\n- Add missing hashPasswordForCookie to AuthenticationGuard [\\#492](https://github.com/auth0/laravel-auth0/pull/492) ([steffjenl](https://github.com/steffjenl))\n\n**Changed**\n\n- Add PHP 8.4 to CI test matrix, cap Psalm <6.5, disable static_lambda CS rule for PHP 8.5+ compat [\\#491](https://github.com/auth0/laravel-auth0/pull/491) ([kishore7snehil](https://github.com/kishore7snehil))\n\n## [7.21.0](https://github.com/auth0/laravel-auth0/tree/7.21.0) (2026-04-01)\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.20.0...7.21.0)\n\n**Fixed**\n\n- Security fix: Resolve CVE-2026-34236\n\n## [7.20.0](https://github.com/auth0/laravel-auth0/tree/7.20.0) (2025-12-16)\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.19.0...7.20.0)\n\n**Fixed**\n\n- Security fix: Resolve CVE-2025-68129\n\n## [7.19.0](https://github.com/auth0/laravel-auth0/tree/7.19.0) (2025-10-01)\n\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.18.0...7.19.0)\n\n**Fixed**\n\n-   Security fix: Resolve CVE-2025-58769\n\n## [7.18.0](https://github.com/auth0/laravel-auth0/tree/7.18.0) (2025-09-02)\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.17.0...7.18.0)\n\n**Added**\n- Mixed changes: feature, fixes, and docs from community [\\#477](https://github.com/auth0/laravel-auth0/pull/477) ([kishore7snehil](https://github.com/kishore7snehil))\n\n## [7.17.0](https://github.com/auth0/laravel-auth0/tree/7.17.0) (2025-05-16)\n\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.16.0...7.17.0)\n\n**Fixed**\n\n-   Security fix: Resolve CVE-2025-47275\n\n## [7.16.0](https://github.com/auth0/laravel-auth0/tree/7.16.0) (2025-04-06)\n\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.15.0...7.16.0)\n\n**Added**\n\n-   Laravel 12 Support [\\#470](https://github.com/auth0/laravel-auth0/pull/470) ([lee-to](https://github.com/lee-to))\n\n**Fixed**\n\n-   refactor: fix failing tests  [\\#471](https://github.com/auth0/laravel-auth0/pull/471) ([noevidenz](https://github.com/noevidenz))\n\n## [7.15.0](https://github.com/auth0/laravel-auth0/tree/7.15.0) (2024-06-03)\n\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.14.0...7.15.0)\n\n**Changed**\n\n-   perf: Update getCredential to only refresh credential once per request [\\#453](https://github.com/auth0/laravel-auth0/pull/453) ([ComputerTinker](https://github.com/ComputerTinker))\n\n## [7.14.0](https://github.com/auth0/laravel-auth0/tree/7.14.0) (2024-04-01)\n\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.13.0...7.14.0)\n\n**Changed**\n\n-   refactor: add additional Telescope state check [\\#447](https://github.com/auth0/laravel-auth0/pull/447) ([samuelhgf](https://github.com/samuelhgf))\n-   chore(deps): replace temporary `psalm-laravel-plugin` fork with official [\\#448](https://github.com/auth0/laravel-auth0/pull/448) ([alies-dev](https://github.com/alies-dev))\n\n## [7.13.0](https://github.com/auth0/laravel-auth0/tree/7.13.0) (2024-03-11)\n\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.12.0...7.13.0)\n\n**Added**\n\n-   Add support for Laravel 11 [\\#445](https://github.com/auth0/laravel-auth0/pull/445) ([evansims](https://github.com/evansims))\n\n**Changed**\n\n-   Verify that Telescope is enabled via configuration helper [\\#444](https://github.com/auth0/laravel-auth0/pull/444) ([samuelhgf](https://github.com/samuelhgf))\n\n## [7.12.0](https://github.com/auth0/laravel-auth0/tree/7.12.0) (2023-12-07)\n\n[Full Changelog](https://github.com/auth0/laravel-auth0/compare/7.11.0...7.12.0)\n\n**Added**\n\n-   Implement support for Back-Channel Logout [\\#435](https://github.com/auth0/laravel-auth0/pull/435) ([evansims](https://github.com/evansims))\n-   Restore configurable route paths [\\#436](https://github.com/auth0/laravel-auth0/pull/436) ([evansims](https://github.com/evansims))\n\n**Fixed**\n\n-   Resolve `CacheBridgeAbstract::save()` not storing values when cache misses [\\#434](https://github.com/auth0/laravel-auth0/pull/434) ([seruymt](https://github.com/seruymt))\n\n## [7.11.0](https://github.com/auth0/laravel-auth0/tree/7.11.0) (2023-08-08)\n\n**Added**\n\n-   Significant performance improvements by eliminating redundant user queries.\n-   Compatibility support for [Laravel Telescope](https://laravel.com/docs/telescope). See [docs/Telescope.md](./docs/Telescope.md) for more information.\n-   A refactored Events API has been introduced. See [docs/Events.md](./docs/Events.md) for more information.\n-   `AUTH0_SESSION_STORAGE` and `AUTH0_TRANSIENT_STORAGE` now support a `cookie` value to enable the native Auth0-PHP SDK cookie session handler. See [docs/Cookies.md](./docs/Cookies.md) for more information.\n\n**Fixed**\n\n-   Addressed an issue where, under certain circumstances, the first user authentication attempt after a session invalidation could fail.\n\n**Changed**\n\n-   Session regeneration/invalidation has been refactored.\n-   Discarded sessions are now deleted when they are invalidated by the SDK, rather than wait for Laravel to garbage collect.\n-   Session storage has been refactored. Session data is now stored as a JSON array in a single `auth0_session` entry in the Laravel session store, rather than in multiple keys.\n\n**Documentation**\n\n-   A demonstration Eloquent user model and repository implementation has been added to [docs/Eloquent.md](./docs/Eloquent.md).\n-   A new [docs/Sessions.md](./docs/Sessions.md) document has been added for guidance on the various session driver options available.\n\n## [7.10.1](https://github.com/auth0/laravel-auth0/tree/7.10.1) (2023-08-07)\n\n**Fixed**\n\n-   Addressed an issue where, under certain circumstances, permissions state could be lost after authenticating.\n\n## [7.10.0](https://github.com/auth0/laravel-auth0/tree/7.10.0) (2023-07-24)\n\n**Added**\n\n-   Organization Name support added for Authentication API and token handling ¹\n\n**Changed**\n\n-   Guards are now registered with the priority middleware list.\n-   Bumped `auth0-php` dependency version range to `^8.7`.\n-   Updated telemetry to indicate new `laravel` package name (previously `laravel-auth0`.)\n\n**Fixed**\n\n-   Addressed issue where placeholder `AUTH0_` dotenv values could erroneously be interpreted as true configuration values.\n\n> **Note**\n> ¹ To use this feature, an Auth0 tenant must have support for it enabled. This feature is not yet available to all tenants.\n\n## [7.9.1](https://github.com/auth0/laravel-auth0/tree/7.9.1) (2023-06-21)\n\n**Fixed**\n\n-   Resolved an issue where, under certain circumstances, the AuthenticationGuard middleware could get erroneously added to the `api` middleware group, causing a session to be established in a stateless request. ([\\#415](https://github.com/auth0/laravel-auth0/pull/415))\n\n## [7.9.0](https://github.com/auth0/laravel-auth0/tree/7.9.0) (2023-06-15)\n\n**Added**\n\n-   SDK configuration (`config/auth0.php`) now supports a `configurationPath` property for specifying a custom search path for `.auth0.*.json` and `.env*` files. ([\\#407](https://github.com/auth0/laravel-auth0/pull/407))\n-   `Auth0\\Laravel\\Guards\\GuardAbstract` now extends `Illuminate\\Contracts\\Auth\\Guard`. ([\\#410](https://github.com/auth0/laravel-auth0/pull/410))\n\n**Fixed**\n\n-   Resolved host environment variables not being loaded as expected when a `.env` file is also used. ([\\#408](https://github.com/auth0/laravel-auth0/pull/408))\n-   Resolved surrounding quote characters not being trimmed from environment variables and `.env` files during processing. ([\\#409](https://github.com/auth0/laravel-auth0/pull/409))\n\n## [7.8.1](https://github.com/auth0/laravel-auth0/tree/7.8.1) (2023-05-19)\n\n**Fixed**\n\n-   Resolved an issue where parsing `.env` files could sometimes throw an exception when handling non-key-value pair strings. ([\\#395](https://github.com/auth0/laravel-auth0/pull/395))\n\n## [7.8.0](https://github.com/auth0/laravel-auth0/tree/7.8.0) (2023-05-18)\n\n**Added**\n\n-   This release adds support for authenticating using **[Pushed Authorization Requests](https://www.rfc-editor.org/rfc/rfc6749)**.\n\n-   This release introduces **two new Authentication Guards** which provide a streamlined integration experience for developers that need to simultaneously support both session-based authentication and token-based endpoint authorization in their Laravel applications.\n\n    | Guard                 | Class                                           | Description                   |\n    | --------------------- | ----------------------------------------------- | ----------------------------- |\n    | `auth0.authenticator` | `Auth0\\Laravel\\Auth\\Guards\\AuthenticationGuard` | Session-based authentication. |\n    | `auth0.authorizer`    | `Auth0\\Laravel\\Auth\\Guards\\AuthorizationGuard`  | Token-based authorization.    |\n\n-   These guards are compatible with Laravel's Authentication API and support the standard `auth` middleware.\n\n-   These guards are compatible with Laravel's Authorization API and support the standard `can` middleware, and the `Guard` facade, and work with the Policies API.\n\n-   3 new pre-built Guards are available: `scope` and `permission`, as well as a dynamic `*:*`. This enables you to verify whether the user's access token has a particular scope or (if RBAC is enabled on the Auth0 API) a particular permission. For example `Gate::check('scope', 'email')` or `Route::get(/*...*/)->can('read:messages')`.\n\n-   The SDK now automatically registers these guards to Laravel's standard `web` and `api` middleware groups, respectively. Manual Guard setup in `config/auth.php` is no longer necessary.\n\n-   The SDK now automatically registers the Authentication routes. Manual route setup in `routes/web.php` is no longer necessary.\n\n-   2 new routing Middleware have been added: `Auth0\\Laravel\\Http\\Middleware\\AuthenticatorMiddleware` and `Auth0\\Laravel\\Http\\Middleware\\AuthorizerMiddleware`. These are automatically registered with your Laravel application, and ensure the Auth0 Guards are used for authentication for `web` routes and authorization for `api` routes, respectively. This replaces the need for the `guard` middleware or otherwise manual Guard assignment in your routes.\n\n**Changed**\n\n-   We've introduced **a new configuration syntax**. This new syntax is more flexible and allows for more complex configuration scenarios, and introduces support for multiple guard instances. Developers using the previous syntax will have their existing configurations applied to all guards uniformly.\n\n-   The SDK can now **configure itself using a `.auth0.json` file in the project root directory**. This file can be generated [using the Auth0 CLI](./docs/Configuration.md), and provides a significantly simpler configuration experience for developers.\n\n-   The previous `auth0.guard` Guard (`Auth0\\Laravel\\Auth\\Guard`) has been **refactored** as a lightweight wrapper around the new `AuthenticationGuard` and `AuthorizationGuard` guards.\n\n## [7.7.0](https://github.com/auth0/laravel-auth0/tree/7.7.0) (2023-04-26)\n\n**Added**\n\n-   `Auth0\\Laravel\\Auth0` now has a `management()` shortcut method for issuing Management API calls. ([\\#376](https://github.com/auth0/laravel-auth0/pull/376))\n\n-   `Auth0\\Laravel\\Auth0\\Guard` now has a `refreshUser()` method for querying `/userinfo` endpoint and refreshing the authenticated user's cached profile data. ([\\#375](https://github.com/auth0/laravel-auth0/pull/375))\n\n-   `Auth0\\Laravel\\Http\\Controller\\Stateful\\Login` now raises a `LoginAttempting` event, offering an opportunity to customize the authorization parameters before the login redirect is issued. ([\\#382](https://github.com/auth0/laravel-auth0/pull/382))\n\n**Changed**\n\n-   The `tokenCache`, `managementTokenCache`, `sessionStorage` and `transientStorage` configuration values now support `false` or `string` values pointing to class names (e.g. `\\Some\\Cache::class`) or class aliases (e.g. `cache.psr6`) registered with Laravel. ([\\#381](https://github.com/auth0/laravel-auth0/pull/381))\n\n## [7.6.0](https://github.com/auth0/laravel-auth0/tree/7.6.0) (2023-04-12)\n\n**Added**\n\n-   `Auth0\\Laravel\\Http\\Middleware\\Guard`, new middleware that forces Laravel to route requests through a group using a specific Guard. ([\\#362](https://github.com/auth0/laravel-auth0/pull/362))\n\n**Changed**\n\n-   `Auth0\\Laravel\\Http\\Middleware\\Stateful\\Authenticate` now remembers the intended route (using `redirect()->setIntendedUrl()`) before kicking off the authentication flow redirect. Users will be returned to the memorized intended route after completing their authentication flow. ([\\#364](https://github.com/auth0/laravel-auth0/pull/364))\n\n**Fixed**\n\n-   legacyGuardUserMethod behavior should use `$session`, not `$token` ([\\#353](https://github.com/auth0/laravel-auth0/pull/365))\n\n## [7.5.2](https://github.com/auth0/laravel-auth0/tree/7.5.2) (2023-04-10)\n\n**Fixed**\n\n-   Relaxed response types from middleware to use low-level `Symfony\\Component\\HttpFoundation\\Response` class, allowing for broader and custom response types.\n\n## [7.5.1](https://github.com/auth0/laravel-auth0/tree/7.5.1) (2023-04-04)\n\n**Fixed**\n\n-   Resolved an issue wherein custom user repositories could fail to be instantiated under certain circumstances.\n\n## [7.5.0](https://github.com/auth0/laravel-auth0/tree/7.5.0) (2023-04-03)\n\nThis release includes support for Laravel 10, and major improvements to the internal state handling mechanisms of the SDK.\n\n**Added**\n\n-   Support for Laravel 10 [#349](https://github.com/auth0/laravel-auth0/pull/349)\n-   New `Auth0\\Laravel\\Traits\\Imposter` trait to allow for easier testing. [Example usage](./tests/Unit/Traits/ImpersonateTest.php)\n-   New Exception types have been added for more precise error-catching.\n\n**Changed**\n\nThe following changes have no effect on the external API of this package but may affect internal usage.\n\n-   `Guard` will now more reliably detect changes in the underlying Auth0-PHP SDK session state.\n-   `Guard` will now more reliably sync changes back to the underlying Auth0-PHP SDK session state.\n-   `StateInstance` concept has been replaced by a new `Credentials` entity.\n-   `Guard` updated to use new `Credentials` entity as primary internal storage for user data.\n-   `Auth0\\Laravel\\Traits\\ActingAsAuth0User` was updated to use new `Credentials` entity.\n-   The HTTP middleware has been refactored to more clearly differentiate between token and session-based identities.\n-   The `authenticate`, `authenticate.optional` and `authorize.optional` HTTP middleware now supports scope filtering, as `authorize` already did.\n\n-   Upgraded test suite to use PEST 2.0 framework.\n-   Updated test coverage to 100%.\n\n**Fixed**\n\n-   A 'Session store not set on request' error could occur when downstream applications implemented unit testing that uses the Guard. This should be resolved now.\n-   `Guard` would not always honor the `provider` configuration value in `config/auth.php`.\n-   `Guard` is no longer defined as a Singleton to better support applications that need multi-guard configurations.\n\n### Notes\n\n#### Changes to `user()` behavior\n\nThis release includes a significant behavior change around the `user()` method of the Guard. Previously, by simply invoking the method, the SDK would search for any available credential (access token, device session, etc.) and automatically assign the user within the Guard. The HTTP middleware has been upgraded to handle the user assignment step, and `user()` now only returns the current state of the user assignment without altering it.\n\nA new property has been added to the `config/auth0.php` configuration file: `behavior`. This is an array. At this time, there is a single option: `legacyGuardUserMethod`, a bool. If this value is set to true, or if the key is missing, the previously expected behavior will be applied, and `user()` will behave as it did before this release. The property defaults to `false`.\n\n#### Changes to Guard and Provider driver aliases\n\nWe identified an issue with using identical alias naming for both the Guard and Provider singletons under Laravel 10, which has required us to rename these aliases. As previous guidance had been to instantiate these using their class names, this should not be a breaking change in most cases. However, if you had used `auth0` as the name for either the Guard or the Provider drivers, kindly note that these have changed. Please use `auth0.guard` for the Guard driver and `auth0.provider` for the Provider driver. This is a regrettable change but was necessary for adequate Laravel 10 support.\n\n## [7.4.0](https://github.com/auth0/laravel-auth0/tree/7.4.0) (2022-12-12)\n\n**Added**\n\n-   feat: Add `Auth0\\Laravel\\Event\\Middleware\\...` event hooks [\\#340](https://github.com/auth0/laravel-auth0/pull/340)\n-   feat: Add `Auth0\\Laravel\\Event\\Configuration\\Building` event hook [\\#339](https://github.com/auth0/laravel-auth0/pull/339)\n\n## [7.3.0](https://github.com/auth0/laravel-auth0/tree/7.3.0) (2022-11-07)\n\n**Added**\n\n-   add: Raise additional Laravel Auth Events [\\#331](https://github.com/auth0/laravel-auth0/pull/331)\n\n**Fixed**\n\n-   fix: `env()` incorrectly assigns `cookieExpires` to a `string` value [\\#332](https://github.com/auth0/laravel-auth0/pull/332)\n-   fix: Auth0\\Laravel\\Cache\\LaravelCachePool::createItem returning a cache miss [\\#329](https://github.com/auth0/laravel-auth0/pull/329)\n\n## [7.2.2](https://github.com/auth0/laravel-auth0/tree/7.2.2) (2022-10-19)\n\n**Fixed**\n\n-   Restore `php artisan vendor:publish` command [\\#321](https://github.com/auth0/laravel-auth0/pull/321)\n-   Bump minimum `auth0/auth0-php` version to `^8.3.4` [\\#322](https://github.com/auth0/laravel-auth0/pull/322)\n\n## [7.2.1](https://github.com/auth0/laravel-auth0/tree/7.2.1) (2022-10-13)\n\n**Fixed**\n\n-   `Auth0\\Laravel\\Auth0` no longer requires a session configuration for stateless strategies, restoring previous behavior. [\\#317](https://github.com/auth0/laravel-auth0/pull/317)\n-   The SDK now requires `^3.0` of the `psr/cache` dependency, to accommodate breaking changes made in the upstream interface (typed parameters and return types) for PHP 8.0+. [\\#316](https://github.com/auth0/laravel-auth0/pull/316)\n\n## [7.2.0](https://github.com/auth0/laravel-auth0/tree/7.2.0) (2022-10-10)\n\n**Changed**\n\n-   `Auth0\\Laravel\\Store\\LaravelSession` has been added as the default `sessionStorage` and `transientStorage` interfaces for the underlying [Auth0-PHP SDK](https://github.com/auth0/auth0-PHP/). The SDK now leverages the native [Laravel Session APIs](https://laravel.com/docs/9.x/session) by default. [\\#307](https://github.com/auth0/laravel-auth0/pull/307)¹\n-   `Auth0\\Laravel\\Cache\\LaravelCachePool` and `Auth0\\Laravel\\Cache\\LaravelCacheItem` have been added as the default `tokenCache` and `managementTokenCache` interfaces for the underlying [Auth0-PHP SDK](https://github.com/auth0/auth0-PHP/). The SDK now leverages the native [Laravel Cache APIs](https://laravel.com/docs/9.x/cache) by default. [\\#307](https://github.com/auth0/laravel-auth0/pull/307)\n-   `Auth0\\Laravel\\Auth\\Guard` now supports the `viaRemember` method. [\\#306](https://github.com/auth0/laravel-auth0/pull/306)\n-   `Auth0\\Laravel\\Http\\Middleware\\Stateless\\Authorize` now returns a 401 status instead of 403 for unauthenticated users. [\\#304](https://github.com/auth0/laravel-auth0/issues/304)\n-   PHP 8.0 is now the minimum supported runtime version. Please review the [README](README.md) for more information on support windows.\n\n¹ This change may require your application's users to re-authenticate. You can avoid this by changing the `sessionStorage` and `transientStorage` options in your SDK configuration to their previous default instances of `Auth0\\SDK\\Store\\CookieStore`, but it is recommended you migrate to the new `LaravelSession` default.\n\n## [7.1.0](https://github.com/auth0/laravel-auth0/tree/7.1.0) (2022-08-08)\n\n**Changed**\n\n-   Return interfaces instead of concrete classes [\\#296](https://github.com/auth0/laravel-auth0/pull/296)\n-   change: Use class names for `app()` calls [\\#291](https://github.com/auth0/laravel-auth0/pull/291)\n\n**Fixed**\n\n-   Fix: `Missing Code` error on Callback Route for Octane Customers [\\#297](https://github.com/auth0/laravel-auth0/pull/297)\n\n## [7.0.1](https://github.com/auth0/laravel-auth0/tree/7.0.1) (2022-06-01)\n\n**Fixed**\n\n-   Fixed an issue in `Auth0\\Laravel\\Http\\Controller\\Stateful\\Callback` where `$errorDescription`'s value was assigned an incorrect value when an error was encountered. [\\#266](https://github.com/auth0/laravel-auth0/pull/288)\n\n## [7.0.0](https://github.com/auth0/laravel-auth0/tree/7.0.0) (2022-03-21)\n\nAuth0 Laravel SDK v7 includes many significant changes over previous versions:\n\n-   Support for Laravel 9.\n-   Support for Auth0-PHP SDK 8.\n-   New authentication route controllers for plug-and-play login support.\n-   Improved authentication middleware for regular web applications.\n-   New authorization middleware for token-based backend API applications.\n\nAs expected with a major release, Auth0 Laravel SDK v7 includes breaking changes. Please review the [upgrade guide](UPGRADE.md) thoroughly to understand the changes required to migrate your application to v7.\n\n### Breaking Changes\n\n-   Namespace has been updated from `Auth0\\Login` to `Auth0\\Laravel`\n-   Auth0-PHP SDK dependency updated to V8\n-   New configuration format\n-   SDK now self-registers its services and middleware\n-   New UserProvider API\n\n> Changelog entries for releases prior to 8.0 have been relocated to [CHANGELOG.ARCHIVE.md](CHANGELOG.ARCHIVE.md).\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2023 Auth0, Inc. <support@auth0.com> (https://auth0.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![Auth0 Laravel SDK](https://cdn.auth0.com/website/sdks/banners/laravel-auth0-banner.png)\n\n<div aria-label=\"Laravel SDK for Auth0 Authentication and Management APIs\">\n    <p aria-hidden=\"true\" align=\"right\">\n        <a href=\"https://github.com/auth0/laravel-auth0/actions/workflows/tests.yml\"><img src=\"https://github.com/auth0/laravel-auth0/actions/workflows/tests.yml/badge.svg\" alt=\"Build Status\"></a>\n        <a href=\"https://codecov.io/gh/auth0/laravel-auth0\"><img src=\"https://codecov.io/gh/auth0/laravel-auth0/branch/main/graph/badge.svg?token=vEwn6TPADf\" alt=\"Code Coverage\"></a>\n        <a href=\"https://packagist.org/packages/auth0/laravel-auth0\"><img src=\"https://img.shields.io/packagist/dt/auth0/login\" alt=\"Total Downloads\"></a>\n        <a href=\"https://packagist.org/packages/auth0/login\"><img src=\"https://img.shields.io/packagist/l/auth0/login\" alt=\"License\"></a>\n    </p>\n</div>\n\n**The Auth0 Laravel SDK is a PHP package that integrates [Auth0](https://auth0.com) into your Laravel application.** It includes no-code user authentication, extensive Management API support, permissions-based routing access control, and more.\n\n-   [Requirements](#requirements)\n-   [Getting Started](#getting-started)\n    -   [1. Install the SDK](#1-install-the-sdk)\n    -   [2. Install the CLI](#2-install-the-cli)\n    -   [3. Configure the SDK](#3-configure-the-sdk)\n    -   [4. Run the Application](#4-run-the-application)\n-   [Documentation](#documentation)\n-   [QuickStarts](#quickstarts)\n-   [Contributing](#contributing)\n-   [Code of Conduct](#code-of-conduct)\n-   [Security](#security)\n-   [License](#license)\n\n## Requirements\n\nYour application must use a [supported Laravel version](#supported-laravel-releases), and your host environment must be running a [maintained PHP version](https://www.php.net/supported-versions.php). Please review [our support policy](./docs/Support.md) for more information.\n\nYou will also need [Composer](https://getcomposer.org/) and an [Auth0 account](https://auth0.com/signup).\n\n### Supported Laravel Releases\n\nThe next major release of Laravel is forecasted for Q1 2025. We anticipate supporting it upon release.\n\n| Laravel                                        | SDK   | PHP                                            | Supported Until                                                                                  |\n| ---------------------------------------------- | ----- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------ |\n| [12.x](https://laravel.com/docs/11.x/releases) | 7.15+ | [8.4](https://www.php.net/releases/8.4/en.php) | Approx. [Feb 2027](https://laravel.com/docs/12.x/releases#support-policy) (EOL for Laravel 12) |\n|                                                |       | [8.2](https://www.php.net/releases/8.3/en.php) | Approx. [Dec 2025](https://www.php.net/supported-versions.php) (EOL for PHP 8.3)                 |\n\nWe strive to support all actively maintained Laravel releases, prioritizing support for the latest major version with our SDK. If a new Laravel major introduces breaking changes, we may have to end support for past Laravel versions earlier than planned.\n\nAffected Laravel versions will still receive security fixes until their end-of-life date, as announced in our release notes.\n\n### Maintenance Releases\n\nThe following releases are no longer being updated with new features by Auth0, but will continue to receive security updates through their end-of-life date.\n\n| Laravel                                        | SDK        | PHP                                            | Security Fixes Until                                                                   |\n| ---------------------------------------------- | ---------- | ---------------------------------------------- | -------------------------------------------------------------------------------------- |\n| [11.x](https://laravel.com/docs/10.x/releases) | 7.13+ | [8.4](https://www.php.net/releases/8.4/en.php) | [March 2026](https://laravel.com/docs/11.x/releases#support-policy) (EOL for Laravel 11) |\n|                                                |            | [8.3](https://www.php.net/releases/8.3/en.php) | [March 2026](https://laravel.com/docs/11.x/releases#support-policy) (EOL for Laravel 11) |\n|                                                |            | [8.2](https://www.php.net/releases/8.2/en.php) | [Dec 2026](https://www.php.net/supported-versions.php) (EOL for PHP 8.2)               |\n\n### Unsupported Releases\n\nThe following releases are unsupported by Auth0. While they may be suitable for some legacy applications, your mileage may vary. We recommend upgrading to a supported version as soon as possible.\n\n| Laravel                                       | SDK        |\n| --------------------------------------------  | ---------- |\n| [10.x](https://laravel.com/docs/10.x/releases)| 7.5 - 7.12 |\n| [9.x](https://laravel.com/docs/9.x/releases)  | 7.0 - 7.12 |\n| [8.x](https://laravel.com/docs/8.x/releases)  | 7.0 - 7.4  |\n| [7.x](https://laravel.com/docs/7.x/releases)  | 5.4 - 6.5  |\n| [6.x](https://laravel.com/docs/6.x/releases)  | 5.3 - 6.5  |\n| [5.x](https://laravel.com/docs/5.x/releases)  | 2.0 - 6.1  |\n| [4.x](https://laravel.com/docs/4.x/releases)  | 1.x        |\n\n## Getting Started\n\nThe following is our recommended approach to getting started with the SDK. Alternatives are available in [our expanded installation guide](./docs/Installation.md).\n\n### 1. Install the SDK\n\n-   For **new applications**, we offer a quickstart template — a version of the default Laravel 9 starter project pre-configured for use with the Auth0 SDK.\n\n    ```shell\n    composer create-project auth0-samples/laravel auth0-laravel-app && cd auth0-laravel-app\n    ```\n\n-   For **existing applications**, you can install the SDK using Composer.\n\n    ```shell\n    composer require auth0/login:^7 --update-with-all-dependencies\n    ```\n\n    In this case, you will also need to generate an SDK configuration file for your application.\n\n    ```shell\n    php artisan vendor:publish --tag auth0\n    ```\n\n</details>\n\n### 2. Install the CLI\n\n1. Install the [Auth0 CLI](https://github.com/auth0/auth0-cli) to manage your account from the command line.\n\n    ```shell\n    curl -sSfL https://raw.githubusercontent.com/auth0/auth0-cli/main/install.sh | sh -s -- -b .\n    ```\n\n    Move the CLI to a directory in your `PATH` to make it available system-wide.\n\n    ```shell\n    sudo mv ./auth0 /usr/local/bin\n    ```\n\n    <p><small>💡 <em>If you prefer not to move the CLI, simply substitute `auth0` in the CLI steps below with `./auth0`.</small></em></p>\n\n    <details>\n    <summary>Using <a href=\"https://brew.sh/\">Homebrew</a> (macOS)</summary>\n     \n\n    ```shell\n    brew tap auth0/auth0-cli && brew install auth0\n    ```\n\n    </details>\n\n    <details>\n    <summary>Using <a href=\"https://scoop.sh/\">Scoop</a> (Windows)</summary>\n     \n\n    ```cmd\n    scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git\n    scoop install auth0\n    ```\n\n    </details>\n\n2. Authenticate the CLI with your Auth0 account. Choose \"as a user\" if prompted.\n\n    ```shell\n    auth0 login\n    ```\n\n### 3. Configure the SDK\n\n1. Register a new application with Auth0.\n\n    ```shell\n    auth0 apps create \\\n      --name \"My Laravel Application\" \\\n      --type \"regular\" \\\n      --auth-method \"post\" \\\n      --callbacks \"http://localhost:8000/callback\" \\\n      --logout-urls \"http://localhost:8000\" \\\n      --reveal-secrets \\\n      --no-input \\\n      --json > .auth0.app.json\n    ```\n\n2. Register a new API with Auth0.\n\n    ```shell\n    auth0 apis create \\\n      --name \"My Laravel Application API\" \\\n      --identifier \"https://github.com/auth0/laravel-auth0\" \\\n      --offline-access \\\n      --no-input \\\n      --json > .auth0.api.json\n    ```\n\n3. Add the new files to `.gitignore`.\n\n    ```bash\n    echo \".auth0.*.json\" >> .gitignore\n    ```\n\n    <details>\n    <summary>Using Windows PowerShell</summary>\n     \n\n    ```powershell\n    Add-Content .gitignore \"`n.auth0.*.json\"\n    ```\n\n    </details>\n\n    <details>\n    <summary>Using Windows Command Prompt</summary>\n     \n\n    ```cmd\n    echo .auth0.*.json >> .gitignore\n    ```\n\n    </details>\n\n### 4. Run the Application\n\nBoot the application using PHP's built-in web server.\n\n```shell\nphp artisan serve\n```\n\nDirect your browser to [http://localhost:8000](http://localhost:8000) to experiment with the application.\n\n-   **Authentication**  \n    Users can log in or out of the application by visiting the [`/login`](http://localhost:8000/login) or [`/logout`](http://localhost:8000/logout) routes, respectively.\n\n-   **API Authorization**  \n    For simplicity sake, generate a test token using the CLI.\n\n    ```shell\n    auth0 test token \\\n      --audience %IDENTIFIER% \\\n      --scopes \"read:messages\"\n    ```\n\n    <p><small>✋ <em>Substitute <code>%IDENTIFIER%</code> with the identifier of the API you created in step 3 above.</small></em></p>\n\n    Now you can send requests to the `/api` endpoints of the application, including the token as a header.\n\n    ```shell\n    curl --request GET \\\n      --url http://localhost:8000/api/example \\\n      --header 'Accept: application/json' \\\n      --header 'Authorization: Bearer %TOKEN%'\n    ```\n\n      <p><small>✋ <em>Substitute <code>%TOKEN%</code> with the test token returned in the previous step.</small></em></p>\n\n      <details>\n      <summary>Using Windows PowerShell</summary>\n       \n\n    ```powershell\n    Invoke-WebRequest http://localhost:8000/api/example `\n      -Headers @{'Accept' = 'application/json'; 'Authorization' = 'Bearer %TOKEN%'}\n    ```\n\n      </details>\n\nWhen you're ready to deploy your application to production, review [our deployment guide](./docs/Deployment.md) for best practices and advice on securing Laravel.\n\n## Integration Examples\n\n<details>\n<summary><b>User Authentication</b></summary>\n \n\nThe SDK automatically registers all the necessary routes and authentication services within the `web` middleware group of your application to enable users to authenticate without requiring you to write any code.\n\n| Route       | Purpose                            |\n| ----------- | ---------------------------------- |\n| `/login`    | Initiates the authentication flow. |\n| `/logout`   | Logs the user out.                 |\n| `/callback` | Handles the callback from Auth0.   |\n\nIf these routes conflict with your application architecture, you can override this default behavior by [adjusting the SDK configuration](./docs/Configuration.md#route-registration).\n\n---\n\n</details>\n\n<details>\n<summary><b>Route Authorization (Access Control)</b></summary>\n \n\nThe SDK automatically registers its authentication and authorization guards within the `web` and `api` middleware groups for your Laravel application, respectively.\n\nFor `web` routes, you can use Laravel's `auth` middleware to require that a user be authenticated to access a route.\n\n```php\nRoute::get('/private', function () {\n  return response('Welcome! You are logged in.');\n})->middleware('auth');\n```\n\nFor `api` routes, you can use Laravel's `auth` middleware to require that a request be authenticated with a valid bearer token to access a route.\n\n```php\nRoute::get('/api/private', function () {\n  return response()->json(['message' => 'Hello! You included a valid token with your request.']);\n})->middleware('auth');\n```\n\nIn addition to requiring that a user be authenticated, you can also require that the user have specific permissions to access a route, using Laravel's `can` middleware.\n\n```php\nRoute::get('/scope', function () {\n    return response('You have the `read:messages` permission, and can therefore access this resource.');\n})->middleware('auth')->can('read:messages');\n```\n\nPermissions require that [RBAC](https://auth0.com/docs/manage-users/access-control/rbac) be enabled within [your API settings](https://manage.auth0.com/#/apis).\n\n---\n\n</details>\n\n<details>\n<summary><b>Users and Tokens</b></summary>\n \n\nLaravel's `Auth` Facade can be used to retrieve information about the authenticated user or token associated with a request.\n\nFor routes using the `web` middleware group in `routes/web.php`.\n\n```php\nRoute::get('/', function () {\n  if (! auth()->check()) {\n    return response('You are not logged in.');\n  }\n\n  $user = auth()->user();\n  $name = $user->name ?? 'User';\n  $email = $user->email ?? '';\n\n  return response(\"Hello {$name}! Your email address is {$email}.\");\n});\n```\n\nFor routes using the `api` middleware group in `routes/api.php`.\n\n```php\nRoute::get('/', function () {\n  if (! auth()->check()) {\n    return response()->json([\n      'message' => 'You did not provide a token.',\n    ]);\n  }\n\n  return response()->json([\n    'message' => 'Your token is valid; you are authorized.',\n    'id' => auth()->id(),\n    'token' => auth()?->user()?->getAttributes(),\n  ]);\n});\n```\n\n---\n\n</details>\n\n<details>\n<summary><b>Management API Calls</b></summary>\n \n\nOnce you've [authorized your application to make Management API calls](./docs/Management.md#api-application-authorization), you'll be able to engage nearly any of the [Auth0 Management API endpoints](https://auth0.com/docs/api/management/v2) through the SDK.\n\nEach API endpoint has its own SDK class which can be accessed through the Facade's `management()` factory method. For interoperability, network responses from the API are returned as [PSR-7 messages](https://www.php-fig.org/psr/psr-7/). These can be converted into native arrays using the SDK's `json()` method.\n\nFor example, to update a user's metadata, you can call the `management()->users()->update()` method.\n\n```php\nuse Auth0\\Laravel\\Facade\\Auth0;\n\nRoute::get('/colors', function () {\n  $colors = ['red', 'blue', 'green', 'black', 'white', 'yellow', 'purple', 'orange', 'pink', 'brown'];\n\n  // Update the authenticated user with a randomly assigned favorite color.\n  Auth0::management()->users()->update(\n    id: auth()->id(),\n    body: [\n      'user_metadata' => [\n        'color' => $colors[random_int(0, count($colors) - 1)]\n      ]\n    ]\n  );\n\n  // Retrieve the user's updated profile.\n  $profile = Auth0::management()->users()->get(auth()->id());\n\n  // Convert the PSR-7 response into a native array.\n  $profile = Auth0::json($profile);\n\n  // Extract some values from the user's profile.\n  $color = $profile['user_metadata']['color'] ?? 'unknown';\n  $name = auth()->user()->name;\n\n  return response(\"Hello {$name}! Your favorite color is {$color}.\");\n})->middleware('auth');\n```\n\nAll the SDK's Management API methods are [documented here](./docs/Management.md).\n\n</details>\n\n## Documentation\n\n-   [Installation](./docs/Installation.md) — Installing the SDK and generating configuration files.\n-   [Configuration](./docs/Configuration.md) — Configuring the SDK using JSON files or environment variables.\n-   [Sessions](./docs/Sessions.md) — Guidance on deciding which Laravel Session API driver to use.\n-   [Cookies](./docs/Cookies.md) — Important notes about using Laravel's Cookie session driver, and alternative options.\n-   [Management API](./docs/Management.md) — Using the SDK to work with the [Auth0 Management API](https://auth0.com/docs/api/management/v2).\n-   [Users](./docs/Users.md) — Extending the SDK to support persistent storage and [Eloquent](https://laravel.com/docs/eloquent) models.\n-   [Events](./docs/Events.md) — Hooking into SDK [events](https://laravel.com/docs/events) to respond to specific actions.\n-   [Deployment](./docs/Deployment.md) — Deploying your application to production.\n\nYou may find the following integration guidance useful:\n\n-   [Laravel Eloquent](./docs/Eloquent.md) — [Eloquent ORM](https://laravel.com/docs/eloquent) is supported.\n-   [Laravel Octane](./docs/Octane.md) — [Octane](https://laravel.com/docs/octane) is not supported at this time.\n-   [Laravel Telescope](./docs/Telescope.md) — [Telescope](https://laravel.com/docs/telescope) is compatible as of SDK v7.11.0.\n\nYou may also find the following resources helpful:\n\n-   [Auth0 Documentation Hub](https://www.auth0.com/docs)\n-   [Auth0 Management API Explorer](https://auth0.com/docs/api/management/v2)\n-   [Auth0 Authentication API Explorer](https://auth0.com/docs/api/authentication)\n\nContributions to improve our documentation [are welcomed](https://github.com/auth0/laravel-auth0/pull).\n\n## QuickStarts\n\n-   [Session-based Authentication](https://auth0.com/docs/quickstart/webapp/laravel) ([GitHub](https://github.com/auth0-samples/laravel))\n-   [Token-based Authorization](https://auth0.com/docs/quickstart/backend/laravel) ([GitHub](https://github.com/auth0-samples/laravel))\n\n## Community\n\nThe [Auth0 Community](https://community.auth0.com) is where you can get support, ask questions, and share your projects.\n\n## Contributing\n\nWe appreciate feedback and contributions to this library. Before you get started, please review Auth0's [General Contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md).\n\nThe [Contribution Guide](./.github/CONTRIBUTING.md) contains information about our development process and expectations, insight into how to propose bug fixes and improvements, and instructions on how to build and test changes to the library.\n\nTo provide feedback or report a bug, [please raise an issue](https://github.com/auth0/laravel-auth0/issues).\n\n## Code of Conduct\n\nParticipants are expected to adhere to Auth0's [Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md) when interacting with this project.\n\n## Security\n\nIf you believe you have found a security vulnerability, we encourage you to responsibly disclose this and not open a public issue. We will investigate all reports. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues.\n\n## License\n\nThis library is open-sourced software licensed under the [MIT license](./LICENSE.md).\n\n---\n\n<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"https://cdn.auth0.com/website/sdks/logos/auth0_light_mode.png\" width=\"150\">\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://cdn.auth0.com/website/sdks/logos/auth0_dark_mode.png\" width=\"150\">\n    <img alt=\"Auth0 Logo\" src=\"https://cdn.auth0.com/website/sdks/logos/auth0_light_mode.png\" width=\"150\">\n  </picture>\n</p>\n\n<p align=\"center\">Auth0 is an easy-to-implement, adaptable authentication and authorization platform.<br />To learn more, check out <a href=\"https://auth0.com/why-auth0\">\"Why Auth0?\"</a></p>\n"
  },
  {
    "path": "UPGRADE.md",
    "content": "# Upgrade Guide\n\n## v7 Migration Guide\n\nAuth0 Laravel SDK v7 includes many significant changes over previous versions:\n\n- Support for Laravel 9.\n- Support for Auth0-PHP SDK 8.\n- New authentication route controllers for plug-and-play login support.\n- Improved authentication middleware for regular web applications.\n- New authorization middleware for token-based backend API applications.\n\nAs expected with a major release, Auth0 Laravel SDK v7 includes breaking changes. Please review this guide thoroughly to undrstand the changes required to migrate your application to v7.\n\n---\n\n### Before you begin: Updated Requirements\n\n- Laravel 8 and Laravel 9 are supported by the Auth0 Laravel SDK v7 release.\n- PHP ≥7.4 is supported by the SDK when paired with Laravel 8.\n- PHP ≥8.0 is supported by the SDK when paired with Laravel 9.¹\n\n¹ This is a requirement of Laravel itself; only PHP 8+ will be supported going forward.\n\n---\n\n### Breaking Changes Summary\n\n- Namespace has been updated from `Auth0\\Login` to `Auth0\\Laravel`.\n- The Auth0-PHP SDK dependency has been updated from V7 to V8, which [may introduce breaking API changes](https://github.com/auth0/auth0-PHP/blob/main/UPGRADE.md) that will require further changes in your app outside the scope of this Laravel SDK.\n- A simplified configuration file format is present. You will need to regenerate your config file. (Instructions below.)\n- Changes to application files are no longer necessary, as the SDK registers services and middleware itself. You should remove any `config/app.php` or `app/HttpKernel.php` customizations made to avoid conflicts. (Instructions below.)\n\n---\n\n### Migration Guidance\n\n#### Update Configuration Scheme\n\n- Configuration filename is now `config/auth0.php`.\n- Configuration format has been updated to support Auth0-PHP SDK 8.\n\n1. Delete any previous laravel-auth0 configuration files present in your application.\n2. Use `php artisan vendor:publish --tag=auth0-config` to generate an updated config file.\n3. Review new configuration instructions in the [README](README.md#configuration-the-sdk).\n\n#### Remove `config\\app.php` modifications\n\n- Previously, the SDK required you to add service provider classes to the `providers` array in this file.\n- This is no longer necessary, as the SDK now registers services itself.\n\n1. Remove any references to the SDK in your `providers` array.\n\n#### Remove `app\\Http\\Kernel.php` modifications\n\n- Previously, the SDK required you to add middleware classes to the middleware arrays in this file.\n- This is no longer necessary, as the SDK now registers these itself.\n\n1. Remove any references to the SDK in your `middleware` arrays.\n2. Update any router middleware references in your app to the types instructed in the [README](README.md#protecting-routes-with-middleware).\n\n#### Update to new authentication routes, as appropriate\nNote: This only applies to regular web application types.\n\n- Previously, the SDK required you to write boilerplate around login, logout and callback routes.\n- The SDK now provides plug-and-play middleware that handles authentication flows, appropriate for most application needs.\n\n1. Remove any route logic around login, logout or callback routes.\n2. Implement the new authentication utility routes as instructed in the [README](README.md#authentication-routes).\n\n#### Update to new `auth0.authenticate` middleware, as appropriate\nNote: This only applies to regular web application types.\n\n- Previously, the SDK advised you to register the Auth0 authentication middleware yourself in the `app\\Http\\Kernel.php`, which invited you to specify custom naming schemes for these middlewares.\n- The SDK now provides plug-and-play middleware with specific naming schemes.\n\n1. Update middleware references from previous custom registrations to the new scheme, as instructed in the [README](README.md#regular-web-applications-1).\n\n\n#### Update to new `auth0.authorize` middleware, as appropriate\nNote: This only applies to backend api application types.\n\n- Previously, the SDK advised you to write your own Access Token handling middleware using the `decodeJWT()` method from the Auth0 PHP SDK.\n- The SDK now provides plug-and-play middleware that handles common endpoint authorization, appropriate for most application needs.\n\n1. Remove custom JWT processing or boilerplate code, particularly those referencing `decodeJWT()` from the old Auth0 PHP SDK releases.\n2. Add new `middleware()` calls to your routes that reference the new SDK authorization middleware, as instructed in the [README](README.md#backend-api-applications-1).\n\n#### Upgrade Auth0-PHP dependency from 7 to 8, as appropriate\n\n- Previous versions of the SDK implemented v7 of the Auth0-PHP SDK dependency.\n- The SDK now uses Auth0-PHP SDK v8.\n\nIf you wrote custom code around the underlying Auth0-PHP, or otherwise made internal calls to the underlying SDK through the Laravel SDK, your application will require further upgrade steps. [Please review the upgrade guide for that SDK here.](https://github.com/auth0/auth0-PHP/blob/main/UPGRADE.md)\n"
  },
  {
    "path": "codecov.yml",
    "content": "codecov:\n  range: \"95...100\"\n  status:\n    project:\n    patch:\n    changes:\nignore:\n  - \"src/Contract\"\n  - \"src/Event\"\n  - \"src/Exception\"\n  - \"src/Http\"\n  - \"src/Model\"\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"auth0/login\",\n    \"description\": \"Auth0 Laravel SDK. Straight-forward and tested methods for implementing authentication, and accessing Auth0's Management API endpoints.\",\n    \"license\": \"MIT\",\n    \"type\": \"library\",\n    \"keywords\": [\n        \"laravel\",\n        \"auth0\",\n        \"authentication\",\n        \"authorization\",\n        \"login\",\n        \"auth\",\n        \"jwt\",\n        \"json web token\",\n        \"jwk\",\n        \"json web key\",\n        \"oauth\",\n        \"openid\",\n        \"secure\",\n        \"protect\",\n        \"api\"\n    ],\n    \"authors\": [\n        {\n            \"name\": \"Auth0\",\n            \"email\": \"support@auth0.com\",\n            \"homepage\": \"https://auth0.com/\"\n        }\n    ],\n    \"homepage\": \"https://github.com/auth0/laravel-auth0\",\n    \"support\": {\n        \"email\": \"support@auth0.com\",\n        \"issues\": \"https://github.com/auth0/laravel-auth0/issues\",\n        \"forum\": \"https://community.auth0.com\",\n        \"source\": \"https://github.com/auth0/laravel-auth0\"\n    },\n    \"require\": {\n        \"php\": \"^8.2\",\n        \"ext-json\": \"*\",\n        \"auth0/auth0-php\": \"^8.19\",\n        \"illuminate/contracts\": \"^11 || ^12 || ^13\",\n        \"illuminate/http\": \"^11 || ^12 || ^13\",\n        \"illuminate/support\": \"^11 || ^12 || ^13\",\n        \"psr-discovery/all\": \"^1\",\n        \"psr/cache\": \"^2 || ^3\"\n    },\n    \"require-dev\": {\n        \"ergebnis/composer-normalize\": \"^2\",\n        \"friendsofphp/php-cs-fixer\": \"^3\",\n        \"larastan/larastan\": \"^2\",\n        \"mockery/mockery\": \"^1\",\n        \"orchestra/testbench\": \"^9\",\n        \"pestphp/pest\": \"^2\",\n        \"pestphp/pest-plugin-laravel\": \"^2\",\n        \"phpstan/phpstan\": \"^1\",\n        \"phpstan/phpstan-strict-rules\": \"^1\",\n        \"psalm/plugin-laravel\": \"^2.12\",\n        \"psr-mock/http\": \"^1\",\n        \"rector/rector\": \"^1\",\n        \"spatie/laravel-ray\": \"^1.40\",\n        \"squizlabs/php_codesniffer\": \"^3\",\n        \"symfony/cache\": \"^6 || ^7\",\n        \"vimeo/psalm\": \"^5 || ^6 <6.5\",\n        \"wikimedia/composer-merge-plugin\": \"^2\"\n    },\n    \"minimum-stability\": \"dev\",\n    \"prefer-stable\": true,\n    \"autoload\": {\n        \"psr-4\": {\n            \"Auth0\\\\Laravel\\\\\": [\n                \"src/\",\n                \"deprecated/\"\n            ]\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Auth0\\\\Laravel\\\\Tests\\\\\": \"tests/\"\n        }\n    },\n    \"config\": {\n        \"allow-plugins\": {\n            \"ergebnis/composer-normalize\": true,\n            \"pestphp/pest-plugin\": true,\n            \"php-http/discovery\": false,\n            \"wikimedia/composer-merge-plugin\": true\n        },\n        \"optimize-autoloader\": true,\n        \"preferred-install\": \"dist\",\n        \"process-timeout\": 0,\n        \"sort-packages\": true\n    },\n    \"extra\": {\n        \"laravel\": {\n            \"aliases\": {\n                \"Auth0\": \"Auth0\\\\Laravel\\\\Facade\\\\Auth0\"\n            },\n            \"providers\": [\n                \"Auth0\\\\Laravel\\\\ServiceProvider\"\n            ]\n        },\n        \"merge-plugin\": {\n            \"ignore-duplicates\": false,\n            \"include\": [\n                \"composer.local.json\"\n            ],\n            \"merge-dev\": true,\n            \"merge-extra\": false,\n            \"merge-extra-deep\": false,\n            \"merge-scripts\": false,\n            \"recurse\": true,\n            \"replace\": true\n        }\n    },\n    \"scripts\": {\n        \"pest\": \"@php vendor/bin/pest --order-by random --fail-on-risky --parallel\",\n        \"pest:coverage\": \"@php vendor/bin/pest --order-by random --fail-on-risky --coverage --parallel\",\n        \"pest:debug\": \"@php vendor/bin/pest --log-events-verbose-text pest.log --display-errors --fail-on-risky\",\n        \"pest:profile\": \"@php vendor/bin/pest --profile\",\n        \"phpcs\": \"@php vendor/bin/php-cs-fixer fix --dry-run --diff\",\n        \"phpcs:fix\": \"@php vendor/bin/php-cs-fixer fix\",\n        \"phpstan\": \"@php vendor/bin/phpstan analyze\",\n        \"psalm\": \"@php vendor/bin/psalm\",\n        \"psalm:fix\": \"@php vendor/bin/psalter --issues=all\",\n        \"rector\": \"@php vendor/bin/rector process src --dry-run\",\n        \"rector:fix\": \"@php vendor/bin/rector process src\",\n        \"test\": [\n            \"@pest\",\n            \"@phpstan\",\n            \"@psalm\",\n            \"@rector\",\n            \"@phpcs\"\n        ]\n    }\n}\n"
  },
  {
    "path": "config/auth0.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Configuration;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\n\nreturn Configuration::VERSION_2 + [\n    'registerGuards' => true,\n    'registerMiddleware' => true,\n    'registerAuthenticationRoutes' => true,\n    'configurationPath' => null,\n\n    'guards' => [\n        'default' => [\n            Configuration::CONFIG_STRATEGY => Configuration::get(Configuration::CONFIG_STRATEGY, SdkConfiguration::STRATEGY_NONE),\n            Configuration::CONFIG_DOMAIN => Configuration::get(Configuration::CONFIG_DOMAIN),\n            Configuration::CONFIG_CUSTOM_DOMAIN => Configuration::get(Configuration::CONFIG_CUSTOM_DOMAIN),\n            Configuration::CONFIG_CLIENT_ID => Configuration::get(Configuration::CONFIG_CLIENT_ID),\n            Configuration::CONFIG_CLIENT_SECRET => Configuration::get(Configuration::CONFIG_CLIENT_SECRET),\n            Configuration::CONFIG_AUDIENCE => Configuration::get(Configuration::CONFIG_AUDIENCE),\n            Configuration::CONFIG_ORGANIZATION => Configuration::get(Configuration::CONFIG_ORGANIZATION),\n            Configuration::CONFIG_USE_PKCE => Configuration::get(Configuration::CONFIG_USE_PKCE),\n            Configuration::CONFIG_SCOPE => Configuration::get(Configuration::CONFIG_SCOPE),\n            Configuration::CONFIG_RESPONSE_MODE => Configuration::get(Configuration::CONFIG_RESPONSE_MODE),\n            Configuration::CONFIG_RESPONSE_TYPE => Configuration::get(Configuration::CONFIG_RESPONSE_TYPE),\n            Configuration::CONFIG_TOKEN_ALGORITHM => Configuration::get(Configuration::CONFIG_TOKEN_ALGORITHM),\n            Configuration::CONFIG_TOKEN_JWKS_URI => Configuration::get(Configuration::CONFIG_TOKEN_JWKS_URI),\n            Configuration::CONFIG_TOKEN_MAX_AGE => Configuration::get(Configuration::CONFIG_TOKEN_MAX_AGE),\n            Configuration::CONFIG_TOKEN_LEEWAY => Configuration::get(Configuration::CONFIG_TOKEN_LEEWAY),\n            Configuration::CONFIG_TOKEN_CACHE => Configuration::get(Configuration::CONFIG_TOKEN_CACHE),\n            Configuration::CONFIG_TOKEN_CACHE_TTL => Configuration::get(Configuration::CONFIG_TOKEN_CACHE_TTL),\n            Configuration::CONFIG_HTTP_MAX_RETRIES => Configuration::get(Configuration::CONFIG_HTTP_MAX_RETRIES),\n            Configuration::CONFIG_HTTP_TELEMETRY => Configuration::get(Configuration::CONFIG_HTTP_TELEMETRY),\n            Configuration::CONFIG_MANAGEMENT_TOKEN => Configuration::get(Configuration::CONFIG_MANAGEMENT_TOKEN),\n            Configuration::CONFIG_MANAGEMENT_TOKEN_CACHE => Configuration::get(Configuration::CONFIG_MANAGEMENT_TOKEN_CACHE),\n            Configuration::CONFIG_CLIENT_ASSERTION_SIGNING_KEY => Configuration::get(Configuration::CONFIG_CLIENT_ASSERTION_SIGNING_KEY),\n            Configuration::CONFIG_CLIENT_ASSERTION_SIGNING_ALGORITHM => Configuration::get(Configuration::CONFIG_CLIENT_ASSERTION_SIGNING_ALGORITHM),\n            Configuration::CONFIG_PUSHED_AUTHORIZATION_REQUEST => Configuration::get(Configuration::CONFIG_PUSHED_AUTHORIZATION_REQUEST),\n            Configuration::CONFIG_BACKCHANNEL_LOGOUT_CACHE => Configuration::get(Configuration::CONFIG_BACKCHANNEL_LOGOUT_CACHE),\n            Configuration::CONFIG_BACKCHANNEL_LOGOUT_EXPIRES => Configuration::get(Configuration::CONFIG_BACKCHANNEL_LOGOUT_EXPIRES),\n        ],\n\n        'api' => [\n            Configuration::CONFIG_STRATEGY => SdkConfiguration::STRATEGY_API,\n        ],\n\n        'web' => [\n            Configuration::CONFIG_STRATEGY => SdkConfiguration::STRATEGY_REGULAR,\n            Configuration::CONFIG_COOKIE_SECRET => Configuration::get(Configuration::CONFIG_COOKIE_SECRET, env('APP_KEY')),\n            Configuration::CONFIG_REDIRECT_URI => Configuration::get(Configuration::CONFIG_REDIRECT_URI, env('APP_URL') . '/callback'),\n            Configuration::CONFIG_SESSION_STORAGE => Configuration::get(Configuration::CONFIG_SESSION_STORAGE),\n            Configuration::CONFIG_SESSION_STORAGE_ID => Configuration::get(Configuration::CONFIG_SESSION_STORAGE_ID),\n            Configuration::CONFIG_TRANSIENT_STORAGE => Configuration::get(Configuration::CONFIG_TRANSIENT_STORAGE),\n            Configuration::CONFIG_TRANSIENT_STORAGE_ID => Configuration::get(Configuration::CONFIG_TRANSIENT_STORAGE_ID),\n        ],\n    ],\n\n    'routes' => [\n        Configuration::CONFIG_ROUTE_INDEX => Configuration::get(Configuration::CONFIG_ROUTE_INDEX, '/'),\n        Configuration::CONFIG_ROUTE_CALLBACK => Configuration::get(Configuration::CONFIG_ROUTE_CALLBACK, '/callback'),\n        Configuration::CONFIG_ROUTE_LOGIN => Configuration::get(Configuration::CONFIG_ROUTE_LOGIN, '/login'),\n        Configuration::CONFIG_ROUTE_AFTER_LOGIN => Configuration::get(Configuration::CONFIG_ROUTE_AFTER_LOGIN, '/'),\n        Configuration::CONFIG_ROUTE_LOGOUT => Configuration::get(Configuration::CONFIG_ROUTE_LOGOUT, '/logout'),\n        Configuration::CONFIG_ROUTE_AFTER_LOGOUT => Configuration::get(Configuration::CONFIG_ROUTE_AFTER_LOGOUT, '/'),\n    ],\n];\n"
  },
  {
    "path": "deprecated/Cache/LaravelCacheItem.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Cache;\n\nuse Auth0\\Laravel\\Bridges\\{CacheItemBridgeAbstract, CacheItemBridgeContract};\nuse DateInterval;\nuse DateTimeImmutable;\nuse DateTimeInterface;\n\nuse function is_int;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Bridges\\CacheItemBridge instead.\n *\n * @internal\n *\n * @api\n */\nfinal class LaravelCacheItem extends CacheItemBridgeAbstract implements CacheItemBridgeContract\n{\n    public function expiresAfter(int | DateInterval | null $time): static\n    {\n        $this->expiration = match (true) {\n            null === $time => new DateTimeImmutable('now +1 year'),\n            is_int($time) => new DateTimeImmutable('now +' . (string) $time . ' seconds'),\n            $time instanceof DateInterval => (new DateTimeImmutable())->add($time),\n        };\n\n        return $this;\n    }\n\n    public function expiresAt(?DateTimeInterface $expiration): static\n    {\n        $this->expiration = $expiration ?? new DateTimeImmutable('now +1 year');\n\n        return $this;\n    }\n\n    public function set(mixed $value): static\n    {\n        $this->value = $value;\n\n        return $this;\n    }\n\n    public static function miss(string $key): self\n    {\n        return new self(\n            key: $key,\n            value: null,\n            hit: false,\n        );\n    }\n}\n"
  },
  {
    "path": "deprecated/Cache/LaravelCachePool.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Cache;\n\nuse Auth0\\Laravel\\Bridges\\{CacheBridgeAbstract, CacheBridgeContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Bridges\\CacheBridge instead.\n *\n * @internal\n *\n * @api\n */\nfinal class LaravelCachePool extends CacheBridgeAbstract implements CacheBridgeContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Auth/Guard.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Auth;\n\nuse Auth0\\Laravel\\Guards\\GuardContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Guards\\GuardContract instead.\n *\n * @api\n */\ninterface Guard extends GuardContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Auth/User/Provider.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Auth\\User;\n\nuse Auth0\\Laravel\\UserProviderContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\UserProviderContract instead.\n *\n * @api\n */\ninterface Provider extends UserProviderContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Auth/User/Repository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Auth\\User;\n\nuse Auth0\\Laravel\\UserRepositoryContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\UserRepositoryContract instead.\n *\n * @api\n */\ninterface Repository extends UserRepositoryContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Auth0.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract;\n\nuse Auth0\\Laravel\\ServiceContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\ServiceContract instead.\n *\n * @api\n */\ninterface Auth0 extends ServiceContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Configuration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract;\n\nuse Auth0\\Laravel\\ConfigurationContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\ConfigurationContract instead.\n *\n * @api\n */\ninterface Configuration extends ConfigurationContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Entities/Credential.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Entities;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Entities\\CredentialEntityContract instead.\n *\n * @api\n */\ninterface Credential extends CredentialEntityContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Auth0Event.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event;\n\nuse Auth0\\Laravel\\Events\\EventContract;\n\n/**\n * @deprecated 7.8.0 - Use Auth0\\Laravel\\Events\\EventContract instead.\n *\n * @api\n */\ninterface Auth0Event extends EventContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Configuration/Building.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Configuration;\n\nuse Auth0\\Laravel\\Events\\Configuration\\BuildingConfigurationEventContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\Configuration\\BuildingConfigurationEvent instead.\n *\n * @api\n */\ninterface Building extends BuildingConfigurationEventContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Configuration/Built.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Configuration;\n\nuse Auth0\\Laravel\\Events\\Configuration\\BuiltConfigurationEventContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\Configuration\\BuiltConfigurationEvent instead.\n *\n * @api\n */\ninterface Built extends BuiltConfigurationEventContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Middleware/StatefulRequest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Middleware;\n\nuse Auth0\\Laravel\\Events\\Middleware\\StatefulMiddlewareRequestContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\Middleware\\StatefulMiddlewareRequest instead.\n *\n * @api\n */\ninterface StatefulRequest extends StatefulMiddlewareRequestContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Middleware/StatelessRequest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Middleware;\n\nuse Auth0\\Laravel\\Events\\Middleware\\StatelessMiddlewareRequestContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\Middleware\\StatelessMiddlewareRequest instead.\n *\n * @api\n */\ninterface StatelessRequest extends StatelessMiddlewareRequestContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Stateful/AuthenticationFailed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\AuthenticationFailedContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\AuthenticationFailed instead.\n *\n * @api\n */\ninterface AuthenticationFailed extends AuthenticationFailedContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Stateful/AuthenticationSucceeded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\AuthenticationSucceededContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\AuthenticationSucceeded instead.\n *\n * @api\n */\ninterface AuthenticationSucceeded extends AuthenticationSucceededContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Stateful/LoginAttempting.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\LoginAttemptingContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\LoginAttempting instead.\n *\n * @api\n */\ninterface LoginAttempting extends LoginAttemptingContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Stateful/TokenExpired.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\TokenExpiredContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenExpired instead.\n *\n * @api\n */\ninterface TokenExpired extends TokenExpiredContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Stateful/TokenRefreshFailed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\TokenRefreshFailedContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenRefreshFailed instead.\n *\n * @api\n */\ninterface TokenRefreshFailed extends TokenRefreshFailedContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Stateful/TokenRefreshSucceeded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\TokenRefreshSucceededContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenRefreshSucceeded instead.\n *\n * @api\n */\ninterface TokenRefreshSucceeded extends TokenRefreshSucceededContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Stateless/TokenVerificationAttempting.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Stateless;\n\nuse Auth0\\Laravel\\Events\\TokenVerificationAttemptingContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenVerificationAttempting instead.\n *\n * @api\n */\ninterface TokenVerificationAttempting extends TokenVerificationAttemptingContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Stateless/TokenVerificationFailed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Stateless;\n\nuse Auth0\\Laravel\\Events\\TokenVerificationFailedContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenVerificationFailed instead.\n *\n * @api\n */\ninterface TokenVerificationFailed extends TokenVerificationFailedContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Event/Stateless/TokenVerificationSucceeded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Event\\Stateless;\n\nuse Auth0\\Laravel\\Events\\TokenVerificationSucceededContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenVerificationSucceeded instead.\n *\n * @api\n */\ninterface TokenVerificationSucceeded extends TokenVerificationSucceededContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Exception/AuthenticationException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Exception;\n\nuse Auth0\\Laravel\\Exceptions\\AuthenticationExceptionContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Exceptions\\AuthenticationException instead.\n *\n * @api\n */\ninterface AuthenticationException extends AuthenticationExceptionContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Exception/GuardException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Exception;\n\nuse Auth0\\Laravel\\Exceptions\\GuardExceptionContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Exceptions\\GuardException instead.\n *\n * @api\n */\ninterface GuardException extends GuardExceptionContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Exception/SessionException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Exception;\n\nuse Auth0\\Laravel\\Exceptions\\SessionExceptionContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Exceptions\\SessionException instead.\n *\n * @api\n */\ninterface SessionException extends SessionExceptionContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Exception/Stateful/CallbackException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Exception\\Stateful;\n\nuse Auth0\\Laravel\\Exceptions\\Controllers\\CallbackControllerExceptionContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Exceptions\\Controllers\\CallbackControllerException instead.\n *\n * @api\n */\ninterface CallbackException extends CallbackControllerExceptionContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Http/Controller/Stateful/Callback.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Http\\Controller\\Stateful;\n\nuse Auth0\\Laravel\\Controllers\\CallbackControllerContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Controllers\\CallbackControllerContract instead.\n *\n * @api\n */\ninterface Callback extends CallbackControllerContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Http/Controller/Stateful/Login.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Http\\Controller\\Stateful;\n\nuse Auth0\\Laravel\\Controllers\\LoginControllerContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Controllers\\LoginControllerContract instead.\n *\n * @api\n */\ninterface Login extends LoginControllerContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Http/Controller/Stateful/Logout.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Http\\Controller\\Stateful;\n\nuse Auth0\\Laravel\\Controllers\\LogoutControllerContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Controllers\\LogoutControllerContract instead.\n *\n * @api\n */\ninterface Logout extends LogoutControllerContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Http/Middleware/Stateful/Authenticate.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Http\\Middleware\\Stateful;\n\nuse Auth0\\Laravel\\Middleware\\AuthenticateMiddlewareContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Middleware\\AuthenticateMiddleware instead.\n *\n * @api\n */\ninterface Authenticate extends AuthenticateMiddlewareContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Http/Middleware/Stateful/AuthenticateOptional.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Http\\Middleware\\Stateful;\n\nuse Auth0\\Laravel\\Middleware\\AuthenticateOptionalMiddlewareContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Middleware\\AuthenticateMiddleware instead.\n *\n * @api\n */\ninterface AuthenticateOptional extends AuthenticateOptionalMiddlewareContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Http/Middleware/Stateless/Authorize.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Http\\Middleware\\Stateless;\n\nuse Auth0\\Laravel\\Middleware\\AuthorizeMiddlewareContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Middleware\\AuthorizeMiddleware instead.\n *\n * @api\n */\ninterface Authorize extends AuthorizeMiddlewareContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Http/Middleware/Stateless/AuthorizeOptional.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Http\\Middleware\\Stateless;\n\nuse Auth0\\Laravel\\Middleware\\AuthorizeOptionalMiddlewareContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Middleware\\AuthorizeOptionalMiddleware instead.\n *\n * @api\n */\ninterface AuthorizeOptional extends AuthorizeOptionalMiddlewareContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Model/Stateful/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Model\\Stateful;\n\nuse Auth0\\Laravel\\Users\\StatefulUserContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Users\\StatefulUserContract instead.\n *\n * @api\n */\ninterface User extends StatefulUserContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Model/Stateless/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Model\\Stateless;\n\nuse Auth0\\Laravel\\Users\\StatelessUserContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Users\\StatelessUserContract instead.\n *\n * @api\n */\ninterface User extends StatelessUserContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/Model/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract\\Model;\n\nuse Auth0\\Laravel\\Users\\UserContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Users\\UserContract instead.\n *\n * @api\n */\ninterface User extends UserContract\n{\n}\n"
  },
  {
    "path": "deprecated/Contract/ServiceProvider.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Contract;\n\nuse Auth0\\Laravel\\ServiceProviderContract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\ServiceProviderContract instead.\n *\n * @api\n */\ninterface ServiceProvider extends ServiceProviderContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Configuration/Building.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Configuration;\n\nuse Auth0\\Laravel\\Events\\Configuration\\{BuildingConfigurationEventAbstract, BuildingConfigurationEventContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\Configuration\\BuildingConfigurationEvent instead\n *\n * @api\n */\nfinal class Building extends BuildingConfigurationEventAbstract implements BuildingConfigurationEventContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Configuration/Built.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Configuration;\n\nuse Auth0\\Laravel\\Events\\Configuration\\{BuiltConfigurationEventAbstract, BuiltConfigurationEventContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\Configuration\\BuiltConfigurationEvent instead\n *\n * @api\n */\nfinal class Built extends BuiltConfigurationEventAbstract implements BuiltConfigurationEventContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Middleware/StatefulRequest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Middleware;\n\nuse Auth0\\Laravel\\Events\\Middleware\\{StatefulMiddlewareRequestAbstract, StatefulMiddlewareRequestContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\Middleware\\StatefulMiddlewareRequest instead\n *\n * @api\n */\nfinal class StatefulRequest extends StatefulMiddlewareRequestAbstract implements StatefulMiddlewareRequestContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Middleware/StatelessRequest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Middleware;\n\nuse Auth0\\Laravel\\Events\\Middleware\\{StatelessMiddlewareRequestAbstract, StatelessMiddlewareRequestContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\Middleware\\StatelessMiddlewareRequest instead\n *\n * @api\n */\nfinal class StatelessRequest extends StatelessMiddlewareRequestAbstract implements StatelessMiddlewareRequestContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Stateful/AuthenticationFailed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\{AuthenticationFailedAbstract, AuthenticationFailedContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\AuthenticationFailed instead\n *\n * @api\n */\nfinal class AuthenticationFailed extends AuthenticationFailedAbstract implements AuthenticationFailedContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Stateful/AuthenticationSucceeded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\{AuthenticationSucceededAbstract, AuthenticationSucceededContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\AuthenticationSucceeded instead\n *\n * @api\n */\nfinal class AuthenticationSucceeded extends AuthenticationSucceededAbstract implements AuthenticationSucceededContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Stateful/LoginAttempting.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\{LoginAttemptingAbstract, LoginAttemptingContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\LoginAttempting instead\n *\n * @api\n */\nfinal class LoginAttempting extends LoginAttemptingAbstract implements LoginAttemptingContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Stateful/TokenExpired.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\{TokenExpiredAbstract, TokenExpiredContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenExpired instead\n *\n * @api\n */\nfinal class TokenExpired extends TokenExpiredAbstract implements TokenExpiredContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Stateful/TokenRefreshFailed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\{TokenRefreshFailedAbstract, TokenRefreshFailedContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenRefreshFailed instead\n *\n * @api\n */\nfinal class TokenRefreshFailed extends TokenRefreshFailedAbstract implements TokenRefreshFailedContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Stateful/TokenRefreshSucceeded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Stateful;\n\nuse Auth0\\Laravel\\Events\\{TokenRefreshSucceededAbstract, TokenRefreshSucceededContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenRefreshSucceeded instead\n *\n * @api\n */\nfinal class TokenRefreshSucceeded extends TokenRefreshSucceededAbstract implements TokenRefreshSucceededContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Stateless/TokenVerificationAttempting.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Stateless;\n\nuse Auth0\\Laravel\\Events\\{TokenVerificationAttemptingAbstract, TokenVerificationAttemptingContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenVerificationAttempting instead\n *\n * @api\n */\nfinal class TokenVerificationAttempting extends TokenVerificationAttemptingAbstract implements TokenVerificationAttemptingContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Stateless/TokenVerificationFailed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Stateless;\n\nuse Auth0\\Laravel\\Events\\{TokenVerificationFailedAbstract, TokenVerificationFailedContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenVerificationFailed instead\n *\n * @api\n */\nfinal class TokenVerificationFailed extends TokenVerificationFailedAbstract implements TokenVerificationFailedContract\n{\n}\n"
  },
  {
    "path": "deprecated/Event/Stateless/TokenVerificationSucceeded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Event\\Stateless;\n\nuse Auth0\\Laravel\\Events\\{TokenVerificationSucceededAbstract, TokenVerificationSucceededContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Events\\TokenVerificationSucceeded instead\n *\n * @api\n */\nfinal class TokenVerificationSucceeded extends TokenVerificationSucceededAbstract implements TokenVerificationSucceededContract\n{\n}\n"
  },
  {
    "path": "deprecated/Exception/AuthenticationException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exception;\n\nuse Auth0\\Laravel\\Exceptions\\{AuthenticationExceptionAbstract, AuthenticationExceptionContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Exceptions\\AuthenticationException instead.\n *\n * @api\n */\nfinal class AuthenticationException extends AuthenticationExceptionAbstract implements AuthenticationExceptionContract\n{\n}\n"
  },
  {
    "path": "deprecated/Exception/GuardException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exception;\n\nuse Auth0\\Laravel\\Exceptions\\{GuardExceptionAbstract, GuardExceptionContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Exceptions\\GuardException instead.\n *\n * @api\n */\nfinal class GuardException extends GuardExceptionAbstract implements GuardExceptionContract\n{\n}\n"
  },
  {
    "path": "deprecated/Exception/SessionException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exception;\n\nuse Auth0\\Laravel\\Exceptions\\{SessionExceptionAbstract, SessionExceptionContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Exceptions\\SessionException instead.\n *\n * @api\n */\nfinal class SessionException extends SessionExceptionAbstract implements SessionExceptionContract\n{\n}\n"
  },
  {
    "path": "deprecated/Exception/Stateful/CallbackException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exception\\Stateful;\n\nuse Auth0\\Laravel\\Exceptions\\Controllers\\{CallbackControllerExceptionAbstract, CallbackControllerExceptionContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Exceptions\\Controllers\\CallbackControllerException instead.\n *\n * @api\n */\nfinal class CallbackException extends CallbackControllerExceptionAbstract implements CallbackControllerExceptionContract\n{\n}\n"
  },
  {
    "path": "deprecated/Http/Controller/Stateful/Callback.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Http\\Controller\\Stateful;\n\nuse Auth0\\Laravel\\Controllers\\{CallbackControllerAbstract, CallbackControllerContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Controllers\\CallbackController instead.\n *\n * @api\n */\nfinal class Callback extends CallbackControllerAbstract implements CallbackControllerContract\n{\n}\n"
  },
  {
    "path": "deprecated/Http/Controller/Stateful/Login.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Http\\Controller\\Stateful;\n\nuse Auth0\\Laravel\\Controllers\\{LoginControllerAbstract, LoginControllerContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Controllers\\LoginController instead.\n *\n * @api\n */\nfinal class Login extends LoginControllerAbstract implements LoginControllerContract\n{\n}\n"
  },
  {
    "path": "deprecated/Http/Controller/Stateful/Logout.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Http\\Controller\\Stateful;\n\nuse Auth0\\Laravel\\Controllers\\{LogoutControllerAbstract, LogoutControllerContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Controllers\\LogoutController instead.\n *\n * @api\n */\nfinal class Logout extends LogoutControllerAbstract implements LogoutControllerContract\n{\n}\n"
  },
  {
    "path": "deprecated/Http/Middleware/Guard.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Http\\Middleware;\n\nuse Auth0\\Laravel\\Middleware\\{GuardMiddlewareAbstract, GuardMiddlewareContract};\n\n/**\n * @deprecated 7.8.0 This middleware is no longer required. Please migrate to using either Auth0\\Laravel\\Guards\\AuthenticationGuard or Auth0\\Laravel\\Guards\\AuthorizationGuard.\n *\n * @api\n */\nfinal class Guard extends GuardMiddlewareAbstract implements GuardMiddlewareContract\n{\n}\n"
  },
  {
    "path": "deprecated/Http/Middleware/Stateful/Authenticate.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Http\\Middleware\\Stateful;\n\nuse Auth0\\Laravel\\Middleware\\{AuthenticateMiddlewareAbstract, AuthenticateMiddlewareContract};\n\n/**\n * @deprecated 7.8.0 This middleware is no longer required. Please migrate to using Auth0\\Laravel\\Guards\\AuthenticationGuard and Laravel's standard `auth` middleware instead.\n *\n * @api\n */\nfinal class Authenticate extends AuthenticateMiddlewareAbstract implements AuthenticateMiddlewareContract\n{\n}\n"
  },
  {
    "path": "deprecated/Http/Middleware/Stateful/AuthenticateOptional.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Http\\Middleware\\Stateful;\n\nuse Auth0\\Laravel\\Middleware\\{AuthenticateOptionalMiddlewareAbstract, AuthenticateOptionalMiddlewareContract};\n\n/**\n * @deprecated 7.8.0 This middleware is no longer required. Please migrate to using Auth0\\Laravel\\Guards\\AuthenticationGuard.\n *\n * @api\n */\nfinal class AuthenticateOptional extends AuthenticateOptionalMiddlewareAbstract implements AuthenticateOptionalMiddlewareContract\n{\n}\n"
  },
  {
    "path": "deprecated/Http/Middleware/Stateless/Authorize.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Http\\Middleware\\Stateless;\n\nuse Auth0\\Laravel\\Middleware\\{AuthorizeMiddlewareAbstract, AuthorizeMiddlewareContract};\n\n/**\n * @deprecated 7.8.0 This middleware is no longer required. Please migrate to using Auth0\\Laravel\\Guards\\AuthorizationGuard, and use Laravel's standard `auth` middleware instead.\n *\n * @api\n */\nfinal class Authorize extends AuthorizeMiddlewareAbstract implements AuthorizeMiddlewareContract\n{\n}\n"
  },
  {
    "path": "deprecated/Http/Middleware/Stateless/AuthorizeOptional.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Http\\Middleware\\Stateless;\n\nuse Auth0\\Laravel\\Middleware\\{AuthorizeOptionalMiddlewareAbstract, AuthorizeOptionalMiddlewareContract};\n\n/**\n * @deprecated 7.8.0 This middleware is no longer required. Please migrate to using Auth0\\Laravel\\Guards\\AuthorizationGuard.\n *\n * @api\n */\nfinal class AuthorizeOptional extends AuthorizeOptionalMiddlewareAbstract implements AuthorizeOptionalMiddlewareContract\n{\n}\n"
  },
  {
    "path": "deprecated/Model/Imposter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Model;\n\nuse Auth0\\Laravel\\Users\\{ImposterUserContract, UserAbstract, UserTrait};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Users\\ImposterUser instead.\n *\n * @api\n */\nfinal class Imposter extends UserAbstract implements ImposterUserContract\n{\n    use UserTrait;\n}\n"
  },
  {
    "path": "deprecated/Model/Stateful/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Model\\Stateful;\n\nuse Auth0\\Laravel\\Users\\{StatefulUserContract, UserAbstract, UserTrait};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Users\\StatefulUser instead.\n *\n * @api\n */\nfinal class User extends UserAbstract implements StatefulUserContract\n{\n    use UserTrait;\n}\n"
  },
  {
    "path": "deprecated/Model/Stateless/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Model\\Stateless;\n\nuse Auth0\\Laravel\\Users\\{StatelessUserContract, UserAbstract, UserTrait};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Users\\StatelessUser instead.\n *\n * @api\n */\nfinal class User extends UserAbstract implements StatelessUserContract\n{\n    use UserTrait;\n}\n"
  },
  {
    "path": "deprecated/Model/User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Model;\n\nuse Auth0\\Laravel\\Users\\UserAbstract;\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Users\\UserAbstract instead.\n *\n * @api\n */\nabstract class User extends UserAbstract\n{\n}\n"
  },
  {
    "path": "deprecated/README.md",
    "content": "# Deprecated Classes\n\nThe classes in this directory are deprecated and are provided here to aid in transitioning to their newer counterparts. Please migrate your application to the new classes, as these will be removed in a future release.\n"
  },
  {
    "path": "deprecated/Store/LaravelSession.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Store;\n\nuse Auth0\\Laravel\\Bridges\\{SessionBridgeAbstract, SessionBridgeContract};\n\n/**\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Bridges\\SessionBridge instead.\n *\n * @internal\n *\n * @api\n */\nfinal class LaravelSession extends SessionBridgeAbstract implements SessionBridgeContract\n{\n}\n"
  },
  {
    "path": "docs/BackchannelLogout.md",
    "content": "# Backchannel Logout\n\nThe Auth0 Laravel SDK supports [Backchannel Logout](https://auth0.com/docs/authenticate/login/logout/back-channel-logout) from v7.12 onward. To use this feature, some additional configuration is necessary:\n\n1. **Add a new route to your application.** This route must be publicly accessible. Auth0 will use it to send backchannel logout requests to your application. For example:\n\n```php\nRoute::post('/backchannel', function (Request $request) {\n    if ($request->has('logout_token')) {\n        app('auth0')->handleBackchannelLogout($request->string('logout_token', '')->trim());\n    }\n});\n```\n\n2. **Configure your Auth0 tenant to use Backchannel Logout.** See the [Auth0 documentation](https://auth0.com/docs/authenticate/login/logout/back-channel-logout/configure-back-channel-logout) for more information on how to do this. Please ensure you point the Logout URI to the backchannel route we just added to your application.\n\nNote: If your application's configuration assigns `false` to the `backchannelLogoutCache` SDK configuration property, this feature will be disabled entirely.\n"
  },
  {
    "path": "docs/Configuration.md",
    "content": "# Configuration\n\n- [SDK Configuration](#sdk-configuration)\n  - [JSON Configuration Files](#json-configuration-files)\n  - [Environment Variables](#environment-variables)\n  - [Order of Priority](#order-of-priority)\n  - [Default Behavior](#default-behavior)\n    - [Guard Registration](#guard-registration)\n    - [Middleware Registration](#middleware-registration)\n    - [Route Registration](#route-registration)\n- [Auth0 Configuration](#auth0-configuration)\n  - [Auth0 Applications](#auth0-applications)\n    - [Creating Applications with the CLI](#creating-applications-with-the-cli)\n    - [Creating Applications Manually](#creating-applications-manually)\n    - [Modifying Applications with the CLI](#modifying-applications-using-the-cli)\n    - [Modifying Applications Manually](#modifying-applications-manually)\n  - [Auth0 APIs](#auth0-apis)\n    - [Creating APIs with the CLI](#creating-apis-with-the-cli)\n    - [Creating APIs Manually](#creating-apis-manually)\n    - [Modifying APIs with the CLI](#modifying-apis-using-the-cli)\n    - [Modifying APIs Manually](#modifying-apis-manually)\n\n## SDK Configuration\n\nThis guide addresses v2 of the SDK configuration format. You can determine which version you are using by evaluating the constant prepended to the returned array in your application's `config/auth0.php` file, prefixed with `Configuration::VERSION_`. For example:\n\n```php\nreturn Configuration::VERSION_2 + [\n  // ...\n];\n```\n\nIf you do not see such a value, you are most likely using an outdated configuration format, and should upgrade by running `php artisan vendor:publish --tag auth0 --force` from your project directory. You will lose any alterations you have made to this file in the process.\n\n### JSON Configuration Files\n\nThe preferred method of SDK configuration is to use JSON exported from the [Auth0 CLI](https://auth0.com/docs/cli). This allows you to use the CLI to manage your Auth0 configuration, and then export the configuration to JSON for use by the SDK.\n\nThe SDK will look for the following files in the project directory, in the order listed:\n\n- `auth0.json`\n- `auth0.<APP_ENV>.json`\n- `auth0.api.json`\n- `auth0.app.json`\n- `auth0.api.<APP_ENV>.json`\n- `auth0.app.<APP_ENV>.json`\n\nWhere `<APP_ENV>` is the value of Laravel's `APP_ENV` environment variable (if set.) Duplicate keys in the files listed above will be overwritten in the order listed.\n\n### Environment Variables\n\nThe SDK also supports configuration using environment variables. These can be defined within the host environment, or using so-called dotenv (`.env`, or `.env.*`) files in the project directory.\n\n| Variable              | Description                                                                                          |\n| --------------------- | ---------------------------------------------------------------------------------------------------- |\n| `AUTH0_DOMAIN`        | `String (FQDN)` The Auth0 domain for your tenant.                                                    |\n| `AUTH0_CUSTOM_DOMAIN` | `String (FQDN)` The Auth0 custom domain for your tenant, if set.                                     |\n| `AUTH0_CLIENT_ID`     | `String` The Client ID for your Auth0 application.                                                   |\n| `AUTH0_CLIENT_SECRET` | `String` The Client Secret for your Auth0 application.                                               |\n| `AUTH0_AUDIENCE`      | `String (comma-delimited list)` The audiences for your application.                                  |\n| `AUTH0_SCOPE`         | `String (comma-delimited list)` The scopes for your application. Defaults to 'openid,profile,email'. |\n| `AUTH0_ORGANIZATION`  | `String (comma-delimited list)` The organizations for your application.                              |\n\nThe following environment variables are supported, but should not be adjusted unless you know what you are doing:\n\n| Variable                                   | Description                                                                                                                                                          |\n| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `AUTH0_USE_PKCE`                           | Boolean. Whether to use PKCE for the authorization flow. Defaults to `true`.                                                                                         |\n| `AUTH0_RESPONSE_MODE`                      | `String` The response mode to use for the authorization flow. Defaults to `query`.                                                                                   |\n| `AUTH0_RESPONSE_TYPE`                      | `String` The response type to use for the authorization flow. Defaults to `code`.                                                                                    |\n| `AUTH0_TOKEN_ALGORITHM`                    | `String` The algorithm to use for the ID token. Defaults to `RS256`.                                                                                                 |\n| `AUTH0_TOKEN_JWKS_URI`                     | `String (URL)` The URI to use to retrieve the JWKS for the ID token. Defaults to `https://<AUTH0_DOMAIN>/.well-known/jwks.json`.                                     |\n| `AUTH0_TOKEN_MAX_AGE`                      | `Integer` The maximum age of a token, in seconds. No default value is assigned.                                                                                      |\n| `AUTH0_TOKEN_LEEWAY`                       | `Integer` The leeway to use when validating a token, in seconds. Defaults to `60` (1 minute).                                                                        |\n| `AUTH0_TOKEN_CACHE`                        | `String (class name)` A PSR-6 class to use for caching JWKS responses.                                                                                               |\n| `AUTH0_TOKEN_CACHE_TTL`                    | `Integer` The TTL to use for caching JWKS responses. Defaults to `60` (1 minute).                                                                                    |\n| `AUTH0_HTTP_MAX_RETRIES`                   | `Integer` The maximum number of times to retry a failed HTTP request. Defaults to `3`.                                                                               |\n| `AUTH0_HTTP_TELEMETRY`                     | `Boolean` Whether to send telemetry data with HTTP requests to Auth0. Defaults to `true`.                                                                            |\n| `AUTH0_SESSION_STORAGE`                    | `String (class name)` The `StoreInterface` class to use for storing session data. Defaults to using Laravel's native Sessions API.                                   |\n| `AUTH0_SESSION_STORAGE_ID`                 | `String` The namespace to use for storing session data. Defaults to `auth0_session`.                                                                                 |\n| `AUTH0_TRANSIENT_STORAGE`                  | `String (class name)` The `StoreInterface` class to use for storing temporary session data. Defaults to using Laravel's native Sessions API.                         |\n| `AUTH0_TRANSIENT_STORAGE_ID`               | `String` The namespace to use for storing temporary session data. Defaults to `auth0_transient`.                                                                     |\n| `AUTH0_MANAGEMENT_TOKEN`                   | `String` The Management API token to use for the Management API client. If one is not provided, the SDK will attempt to create one for you.                          |\n| `AUTH0_MANAGEMENT_TOKEN_CACHE`             | `Integer` A PSR-6 class to use for caching Management API tokens.                                                                                                    |\n| `AUTH0_CLIENT_ASSERTION_SIGNING_KEY`       | `String` The key to use for signing client assertions.                                                                                                               |\n| `AUTH0_CLIENT_ASSERTION_SIGNING_ALGORITHM` | `String` The algorithm to use for signing client assertions. Defaults to `RS256`.                                                                                    |\n| `AUTH0_PUSHED_AUTHORIZATION_REQUEST`       | `Boolean` Whether the SDK should use Pushed Authorization Requests during authentication. Note that your tenant must have this feature enabled. Defaults to `false`. |\n| `AUTH0_BACKCHANNEL_LOGOUT_CACHE`           | `String (class name)` A PSR-6 class to use for caching backchannel logout tokens.                                                                                    |\n| `AUTH0_BACKCHANNEL_LOGOUT_EXPIRES`         | `Integer` How long (in seconds) to cache a backchannel logout token. Defaults to `2592000` (30 days).                                                                |\n\n### Order of Priority\n\nThe SDK collects configuration data from multiple potential sources, in the following order:\n\n- `.auth0.json` files\n- `.env` (dotenv) files\n- Host environment variables\n\n> **Note:**  \n> In the filenames listed below, `%APP_ENV%` is replaced by the application's configured `APP_ENV` environment variable, if one is set.\n\nIt begins by loading matching JSON configuration files from the project's root directory, in the following order:\n\n- `.auth0.json`\n- `.auth0.%APP_ENV%.json`\n- `.auth0.api.json`\n- `.auth0.app.json`\n- `.auth0.api.%APP_ENV%.json`\n- `.auth0.app.%APP_ENV%.json`\n\nIt then loads configuration data from available `.env` (dotenv) configuration files, in the following order.\n\n- `.env`\n- `.env.auth0`\n- `.env.%APP_ENV%`\n- `.env.%APP_ENV%.auth0`\n\nFinally, it loads environment variables from the host environment.\n\nDuplicate configuration data is overwritten by the value from the last source loaded. For example, if the `AUTH0_DOMAIN` environment variable is set in both the `.env` file and the host environment, the value from the host environment will be used.\n\nAlthough JSON configuration keys are different from their associated environment variable counterparts, these are translated automatically by the SDK. For example, the `domain` key in the JSON configuration files is translated to the `AUTH0_DOMAIN` environment variable.\n\n### Default Behavior\n\n#### Guard Registration\n\nBy default, the SDK will register the Authentication and Authorization guards with your Laravel application, as well as a compatible [User Provider](./Users.md).\n\nYou can disable this behavior by setting `registerGuards` to `false` in your `config/auth0.php` file.\n\n```php\nreturn Configuration::VERSION_2 + [\n  'registerGuards' => false,\n  // ...\n];\n```\n\nTo register the guards manually, update the arrays in your `config/auth.php` file to include the following additions:\n\n```php\n'guards' => [\n  'auth0-session' => [\n    'driver' => 'auth0.authenticator',\n    'provider' => 'auth0-provider',\n    'configuration' => 'web',\n  ],\n  'auth0-api' => [\n    'driver' => 'auth0.authorizer',\n    'provider' => 'auth0-provider',\n    'configuration' => 'api',\n  ],\n],\n\n'providers' => [\n  'auth0-provider' => [\n    'driver' => 'auth0.provider',\n    'repository' => 'auth0.repository',\n  ],\n],\n```\n\n#### Middleware Registration\n\nBy default, the SDK will register the Authentication and Authorization guards within your application's `web` and `api` middleware groups.\n\nYou can disable this behavior by setting `registerMiddleware` to `false` in your `config/auth0.php` file.\n\n```php\nreturn Configuration::VERSION_2 + [\n  'registerMiddleware' => false,\n  // ...\n];\n```\n\nTo register the middleware manually, update your `app/Http/Kernel.php` file and include the following additions:\n\n```php\nprotected $middlewareGroups = [\n  'web' => [\n    \\Auth0\\Laravel\\Middleware\\AuthenticatorMiddleware::class,\n    // ...\n  ],\n\n  'api' => [\n    \\Auth0\\Laravel\\Middleware\\AuthorizerMiddleware::class,\n    // ...\n  ],\n];\n```\n\nAlternatively, you can assign the guards to specific routes by using the `Auth` facade. For `routes/web.php`, add the following before any routes:\n\n```php\nAuth::shouldUse('auth0-session');\n```\n\nFor `routes/api.php`, add the following before any routes:\n\n```php\nAuth::shouldUse('auth0-api');\n```\n\n#### Route Registration\n\nBy default, the SDK will register the following routes for authentication:\n\n| Method | URI         | Name       | Controller                                     | Purpose                            |\n| ------ | ----------- | ---------- | ---------------------------------------------- | ---------------------------------- |\n| `GET`  | `/login`    | `login`    | `Auth0\\Laravel\\Controllers\\LoginController`    | Initiates the authentication flow. |\n| `GET`  | `/logout`   | `logout`   | `Auth0\\Laravel\\Controllers\\LogoutController`   | Logs the user out.                 |\n| `GET`  | `/callback` | `callback` | `Auth0\\Laravel\\Controllers\\CallbackController` | Handles the callback from Auth0.   |\n\nYou can disable this behavior by setting `registerAuthenticationRoutes` to `false` in your `config/auth0.php` file.\n\n```php\nreturn Configuration::VERSION_2 + [\n  'registerAuthenticationRoutes' => false,\n  // ...\n];\n```\n\nIf you've disabled the automatic registration of routes, you must register the routes manually for authentication to work.\n\n```php\nuse Auth0\\Laravel\\Controllers\\{LoginController, LogoutController, CallbackController};\n\nRoute::group(['middleware' => ['guard:auth0-session']], static function (): void {\n  Route::get('/login', LoginController::class)->name('login');\n  Route::get('/logout', LogoutController::class)->name('logout');\n  Route::get('/callback', CallbackController::class)->name('callback');\n});\n```\n\nOr you can call the SDK Facade's `routes()` method in your `routes/web.php` file:\n\n```php\nAuth0::routes();\n```\n\n- These must be registered within the `web` middleware group, as they rely on sessions.\n- Requests must be routed through the SDK's Authenticator guard.\n\n## Auth0 Configuration\n\nThe following guidance is provided to help you configure your Auth0 tenant for use with the SDK. It is not intended to be a comprehensive guide to configuring Auth0. Please refer to the [Auth0 documentation](https://auth0.com/docs) for more information.\n\n### Auth0 Applications\n\n#### Creating Applications with the CLI\n\nUse the CLI's `apps create` command to create a new Auth0 Application:\n\n```shell\nauth0 apps create \\\n  --name \"My Laravel Application\" \\\n  --type \"regular\" \\\n  --auth-method \"post\" \\\n  --callbacks \"http://localhost:8000/callback\" \\\n  --logout-urls \"http://localhost:8000\" \\\n  --reveal-secrets \\\n  --no-input\n```\n\nIf you are configuring the SDK for this application, make note of the `client_id` and `client_secret` values returned by the command. Follow the guidance in the [configuration guide](#configuration) to configure the SDK using these values.\n\nThe following parameters used in this example are of note:\n\n- `--type` - The [application type](https://auth0.com/docs/get-started/applications).\n  - For Laravel applications, this should always be set to `regular`.\n- `--auth-method` - This represents the 'Token Endpoint Authentication Method' used for authentication.\n  - For Laravel applications, this should always be set to `post`.\n- `--callbacks` - The callback URLs to use for authentication.\n  - In development, this should be set to `http://localhost:8000/callback` or as appropriate.\n  - In production, adjust this value to match your application's Internet-accessible URL for its`/callback`` route.\n  - This value can be a comma-separated list of URLs.\n- `--logout-urls` - The logout URLs to use for authentication.\n  - In development, this should be set to `http://localhost:8000` or as appropriate.\n  - In production, adjust this value to match where your application redirects end users after logging out. The value should be an Internet-accessible URL.\n  - This value can be a comma-separated list of URLs.\n\nPlease refer to the [CLI documentation](https://auth0.github.io/auth0-cli/auth0_apps_create.html) for additional information on the `apps create` command.\n\n#### Modifying Applications using the CLI\n\nUse the CLI's `apps update` command to create a new Auth0 API:\n\n```shell\nauth0 apps update %CLIENT_ID% \\\n  --name \"My Updated Laravel Application\" \\\n  --callbacks \"https://production/callback,http://localhost:8000/callback\" \\\n  --logout-urls \"https://production/logout,http://localhost:8000\" \\\n  --no-input\n```\n\nSubstitute `%CLIENT_ID%` with your application's Client ID. Depending on how you configured the SDK, this value can be found:\n\n- In the `.auth0.app.json` file in your project's root directory, as the `client_id` property value.\n- In the `.env` file in your project's root directory, as the `AUTH0_CLIENT_ID` property value.\n- As the `AUTH0_CLIENT_ID` environment variable.\n- Evaluating the output from the CLI's `apps list` command.\n\nPlease refer to the [CLI documentation](https://auth0.github.io/auth0-cli/auth0_apps_update.html) for additional information on the `apps update` command.\n\n#### Creating Applications Manually\n\n1. Log in to your [Auth0 Dashboard](https://manage.auth0.com/).\n2. Click the **Applications** menu item in the left navigation bar.\n3. Click the **Create Application** button.\n4. Enter a name for your application.\n5. Select **Regular Web Applications** as the application type.\n6. Click the **Create** button.\n7. Click the **Settings** tab.\n8. Set the **Token Endpoint Authentication Method** to `POST`.\n9. Set the **Allowed Callback URLs** to `http://localhost:8000/callback` or as appropriate.\n10. Set the **Allowed Logout URLs** to `http://localhost:8000` or as appropriate.\n11. Click the **Save Changes** button.\n\n#### Modifying Applications Manually\n\n1. Log in to your [Auth0 Dashboard](https://manage.auth0.com/).\n2. Click the **Applications** menu item in the left navigation bar.\n3. Click the name of the application you wish to modify.\n4. Click the **Settings** tab.\n5. Modify the properties you wish to update as appropriate.\n6. Click the **Save Changes** button.\n\n### Auth0 APIs\n\n#### Creating APIs with the CLI\n\nUse the CLI's `apis create` command to create a new Auth0 API:\n\n```shell\nauth0 apis create \\\n  --name \"My Laravel Application API\" \\\n  --identifier \"https://github.com/auth0/laravel-auth0\" \\\n  --offline-access \\\n  --no-input\n```\n\nIf you are configuring the SDK for this API, make note of the `identifier` you used here. Follow the guidance in the [configuration guide](#configuration) to configure the SDK using this value.\n\nThe following parameters are of note:\n\n- `--identifier` - The [unique identifier](https://auth0.com/docs/get-started/apis/api-settings#general-settings) for your API, sometimes referred to as the `audience`. This can be any value you wish, but it must be unique within your account. It cannot be changed later.\n- `--offline-access` - This enables the use of [Refresh Tokens](https://auth0.com/docs/tokens/refresh-tokens) for your API. This is not required for the SDK to function.\n\nPlease refer to the [CLI documentation](https://auth0.github.io/auth0-cli/auth0_apis_create.html) for additional information on the `apis create` command.\n\n#### Modifying APIs using the CLI\n\nUse the CLI's `apis update` command to create a new Auth0 API:\n\n```shell\nauth0 apis update %IDENTIFIER% \\\n  --name \"My Updated Laravel Application API\" \\\n  --token-lifetime 6100 \\\n  --offline-access=false \\\n  --scopes \"letter:write,letter:read\" \\\n  --no-input\n```\n\nSubstitute `%IDENTIFIER%` with your API's unique identifier.\n\nPlease refer to the [CLI documentation](https://auth0.github.io/auth0-cli/auth0_apis_update.html) for additional information on the `apis update` command.\n\n#### Creating APIs Manually\n\n1. Log in to your [Auth0 Dashboard](https://manage.auth0.com/).\n2. Click the **APIs** menu item in the left navigation bar.\n3. Click the **Create API** button.\n4. Enter a name for your API.\n5. Enter a unique identifier for your API. This can be any value you wish, but it must be unique within your account. It cannot be changed later.\n6. Click the **Create** button.\n\n#### Modifying APIs Manually\n\n1. Log in to your [Auth0 Dashboard](https://manage.auth0.com/).\n2. Click the **APIs** menu item in the left navigation bar.\n3. Click the name of the API you wish to modify.\n4. Modify the properties you wish to update as appropriate.\n5. Click the **Save Changes** button.\n\nAdditional information on Auth0 application settings [can be found here](https://auth0.com/docs/get-started/applications/application-settings).\n"
  },
  {
    "path": "docs/Cookies.md",
    "content": "# Cookies\n\nWe strongly recommend using the `database` or `redis` session drivers, but realize this is not always a viable option for all developers or use cases. The Auth0 Laravel SDK supports cookies for storing authentication state, but there are notable drawbacks to be aware of.\n\n## Laravel's Cookie Session Driver\n\nAs noted in our [sessions documentation](./Sessions.md), Laravel's `cookie` session driver is not a reliable option for production applications as it suffers from a number of notable drawbacks:\n\n- Browsers impose a size limit of 4 KB on individual cookies, which can quickly be exceeded by storing session data.\n- Laravel's cookie driver unfortunately does not \"chunk\" (split up) larger cookies into multiple cookies, so it is impossible to store more than the noted 4 KB of total session data.\n- Most web servers and load balancers require additional configuration to accept and deliver larger cookie headers.\n\n## Auth0 PHP SDK's Custom Cookie Session Handler\n\nThe underlying [Auth0 PHP SDK](https://github.com/auth0/auth0-PHP) (which the Auth0 Laravel SDK is built upon) includes a powerful custom cookie session handler that supports chunking of larger cookies. This approach will enable you to securely and reliably store larger authentication states for your users.\n\nIt is important to note that this approach is incompatible with [Octane](./Octane.md) due to the way it delivers cookie headers.\n\nTo enable this feature, assign a `cookie` string value to the `AUTH0_SESSION_STORAGE` and `AUTH0_TRANSIENT_STORAGE` environment variables (or your `.env` file.)\n\n```ini\n# Persistent session data:\nAUTH0_SESSION_STORAGE=cookie\n\n# Temporary session data (used only during authentication):\nAUTH0_TRANSIENT_STORAGE=cookie\n```\n\nThis will override the SDK's default behavior of using the Laravel Sessions API, and instead use the integrated Auth0 PHP SDK's custom cookie session handler. Please note:\n\n- When this feature is enabled, all properties of cookie storage (like `sameSite`, `secure`, and so forth) must be configured independently. This approach does not use Laravel's settings. Please refer to the [Auth0 PHP SDK's documentation](https://github.com/auth0/auth0-PHP) for guidance on how to configure these.\n- By default your Laravel application's `APP_KEY` will be used to encrypt the cookie data. You can change this by assigning the `AUTH0_COOKIE_SECRET` environment variable (or your `.env` file) a string. If you do this, please ensure you are using an adequately long secure secret.\n- Please ensure your server is configured to deliver and accept cookies prefixed with `auth0_session_` and `auth0_transient_` followed by a series of numbers (beginning with 0). These are the divided content body of the authenticated session data.\n\n### Increasing Server Cookies Header Sizes\n\nYou may need to configure your web server or load balancer to accept and deliver larger cookie headers. For example, if you are using Nginx you will need to set the `large_client_header_buffers` directive to a value greater than the default of 4 KB.\n\n```nginx\nlarge_client_header_buffers 4 16k;\n```\n\nPlease refer to your web server or load balancer's documentation for more information.\n\n### Reminder on Octane Compatibility\n\nAs noted above, the Auth0 PHP SDK's custom cookie session handler is incompatible with [Octane](./Octane.md) due to the way it delivers cookie headers. If you are using Octane, you must use the Laravel Sessions API with a `database` or `redis` driver.\n"
  },
  {
    "path": "docs/Deployment.md",
    "content": "# Deployment\n\nWhen you're preparing to deploy your application to production, there are some basic steps you can take to make sure your application is running as smoothly and securely as possible. In this guide, we'll cover some starting points for making sure your application is deployed properly.\n\n-   [Auth0 Configuration](#auth0-configuration)\n-   [TLS / HTTPS](#tls--https)\n-   [Cookies](#cookies)\n-   [Server Configuration](#server-configuration)\n    -   [Caddy](#caddy)\n    -   [Nginx](#nginx)\n    -   [Apache](#apache)\n-   [Optimization](#optimization)\n    -   [Autoloader](#autoloader)\n    -   [Dependencies](#dependencies)\n    -   [Caching Configuration](#caching-configuration)\n    -   [Caching Events](#caching-events)\n    -   [Caching Routes](#caching-routes)\n    -   [Caching Views](#caching-views)\n    -   [Debug Mode](#debug-mode)\n\n## Auth0 Configuration\n\nWhen migrating your Laravel application from local development to production, you will need to update your Auth0 application's configuration to reflect the new URLs for your application. You can do this by logging into the [Auth0 Dashboard](https://manage.auth0.com/) and updating the following fields:\n\n-   **Allowed Callback URLs**: The URL that Auth0 will redirect to after the user authenticates. This should be set to the Internet-accessible URL of your application's `/callback` route.\n-   **Allowed Logout URLs**: The URL that Auth0 will redirect to after the user logs out. This should be set to an appropriate Internet-accessible URL of your application.\n\nNote that you can include multiple URLs in these fields by separating them with commas, for example `https://example.com/callback,http://localhost:8000/callback`.\n\nSee [the configuration guide](/docs/configuration.md) for additional guidance on updating configuration properties.\n\n## TLS / HTTPS\n\nAuth0 requires that all applications use TLS/HTTPS. This is a requirement for all applications, regardless of whether they are running in production or development, with the exception of applications running on `localhost`. If you are running your application in a development environment, you can use a self-signed certificate. However, you should ensure that your application is running over TLS/HTTPS in production.\n\nLet's Encrypt is a great option for obtaining free TLS/HTTPS certificates for your application. You can find instructions for obtaining a certificate for your server at [https://letsencrypt.org/getting-started/](https://letsencrypt.org/getting-started/).\n\n## Cookies\n\nDepending on the integration approach, you may encounter instances where the cookies delivered by the application exceed the default allowances of your web server. This can result in errors such as `400 Bad Request`. If you encounter this issue, you should increase the header size limits of your web server to accommodate the larger cookies. The server configurations below include examples of how to do this for common web servers.\n\nYou should also ensure your application's `config/session.php` file is configured securely. The default configuration provided by Laravel is a great starting point, but you should double check that the `secure` option is set to `true`, that the `same_site` option is set to `lax` or `strict`, and the `http_only` option is set to `true`.\n\n## Server Configuration\n\nPlease ensure, like all the example configurations provided below, that your web server directs all requests to your application's `public/index.php` file. You should **never** attempt to move the `index.php` file to your project's root, as serving the application from the project root will expose many sensitive configuration files to the public Internet.\n\n### Caddy\n\n```nginx\nexample.com {\n  root * /var/www/example.com/public\n\n  encode zstd gzip\n  file_server\n\n  limits {\n    header 4kb\n  }\n\n  header {\n    X-XSS-Protection \"1; mode=block\"\n    X-Content-Type-Options \"nosniff\"\n    X-Frame-Options \"SAMEORIGIN\"\n  }\n\n  php_fastcgi unix//var/run/php/php8.2-fpm.sock\n}\n```\n\n### Nginx\n\n```nginx\nserver {\n  listen 80;\n  listen [::]:80;\n  server_name example.com;\n  root /var/www/example.com/public;\n\n  add_header X-XSS-Protection \"1; mode=block\";\n  add_header X-Content-Type-Options \"nosniff\";\n  add_header X-Frame-Options \"SAMEORIGIN\";\n\n  large_client_header_buffers 4 32k;\n\n  index index.php;\n\n  charset utf-8;\n\n  location / {\n    try_files $uri $uri/ /index.php?$query_string;\n  }\n\n  location = /favicon.ico { access_log off; log_not_found off; }\n  location = /robots.txt  { access_log off; log_not_found off; }\n\n  error_page 404 /index.php;\n\n  location ~ \\.php$ {\n    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;\n    fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;\n    include fastcgi_params;\n  }\n\n  location ~ /\\.(?!well-known).* {\n    deny all;\n  }\n}\n```\n\n### Apache\n\n```apache\n<VirtualHost *:80>\n  ServerName example.com\n  ServerAdmin admin@example.com\n  DocumentRoot /var/www/html/example.com/public\n\n  LimitRequestFieldSize 16384\n\n  <Directory /var/www/html/example.com>\n    AllowOverride All\n  </Directory>\n\n  <IfModule mod_headers.c>\n    Header set X-XSS-Protection \"1; mode=block\"\n    Header always set X-Content-Type-Options nosniff\n    Header always set X-Frame-Options SAMEORIGIN\n  </IfModule>\n\n  ErrorLog ${APACHE_LOG_DIR}/error.log\n  CustomLog ${APACHE_LOG_DIR}/access.log combined\n</VirtualHost>\n```\n\n## Optimization\n\n### Autoloader\n\nWhen deploying to production, make sure that you are optimizing Composer's class autoloader map so Composer can quickly find the proper file to load for a given class:\n\n```shell\ncomposer install --optimize-autoloader --no-dev\n```\n\nBe sure to use the `--no-dev` option in production. This will prevent Composer from installing any development dependencies your project's dependencies may have.\n\n### Dependencies\n\nYou should include your `composer.lock` file in your project's source control repository. Fo project's dependencies can be installed much faster with this file is present. Your production environment does not run `composer update` directly. Instead, you can run the `composer update` command locally when you want to update your dependencies, and then commit the updated `composer.lock` file to your repository. Be sure you are running the same major PHP version as your production environment, to avoid introducing compatibility issues.\n\nBecause the `composer.lock` file includes specific versions of your dependencies, other developers on your team will be using the same versions of the dependencies as you. This will help prevent bugs and compatibility issues from appearing in production that aren't present during development.\n\n### Caching Configuration\n\nWhen deploying your application to production, you should make sure that you run the config:cache Artisan command during your deployment process:\n\n```shell\nphp artisan config:cache\n```\n\nThis command will combine all of Laravel's configuration files into a single, cached file, which greatly reduces the number of trips the framework must make to the filesystem when loading your configuration values.\n\n### Caching Events\n\nIf your application is utilizing event discovery, you should cache your application's event to listener mappings during your deployment process. This can be accomplished by invoking the event:cache Artisan command during deployment:\n\n```shell\nphp artisan event:cache\n```\n\n### Caching Routes\n\nIf you are building a large application with many routes, you should make sure that you are running the route:cache Artisan command during your deployment process:\n\n```shell\nphp artisan route:cache\n```\n\nThis command reduces all of your route registrations into a single method call within a cached file, improving the performance of route registration when registering hundreds of routes.\n\n### Caching Views\n\nWhen deploying your application to production, you should make sure that you run the view:cache Artisan command during your deployment process:\n\n```shell\nphp artisan view:cache\n```\n\nThis command precompiles all your Blade views so they are not compiled on demand, improving the performance of each request that returns a view.\n\n## Debug Mode\n\nThe debug option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your application's .env file.\n\n**In your production environment, this value should always be `false`. If the `APP_DEBUG` variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.**\n"
  },
  {
    "path": "docs/Eloquent.md",
    "content": "# Laravel Eloquent\n\nBy default, the SDK does not include any Eloquent models or database support. You can adapt the SDK to your application's needs by adding your own Eloquent models and database support.\n\n## Creating a User Model\n\nBegin by creating a new Eloquent model class. You can use the `make:model` Artisan command to do this. Laravel ships with default user model named `User`, so we'll use the `--force` flag to overwrite it with our custom one.\n\nPlease ensure you have a backup of your existing `User` model before running this command, as it will overwrite your existing model.\n\n```bash\nphp artisan make:model User --force\n```\n\nNext, open your `app/Models/User.php` file and modify it match the following example. Notably, we'll add a support for a new `auth0` attribute. This attribute will be used to store the user's Auth0 ID, which is used to uniquely identify the user in Auth0.\n\n```php\n<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Models;\n\nuse Illuminate\\Auth\\Authenticatable;\nuse Illuminate\\Contracts\\Auth\\Access\\Authorizable as AuthorizableContract;\nuse Illuminate\\Contracts\\Auth\\Authenticatable as AuthenticatableContract;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Foundation\\Auth\\Access\\Authorizable;\n\nclass User extends Model implements\n    AuthenticatableContract,\n    AuthorizableContract\n{\n    use Authenticatable,\n        Authorizable,\n        HasFactory;\n\n    protected $fillable = [\n        'auth0',\n        'name',\n        'email',\n        'email_verified',\n        'password',\n    ];\n\n    protected $hidden = [\n        'password',\n        'remember_token',\n    ];\n\n    protected $casts = [\n        'email_verified_at' => 'datetime',\n    ];\n}\n```\n\nNext, create a migration to update your application's `users` table schema to support these changes. Create a new migration file:\n\n```bash\nphp artisan make:migration update_users_table --table=users\n```\n\nOpenly the newly created migration file (found under `database/migrations` and ending in `update_users_table.php`) and update to match the following example:\n\n```php\n<?php\n\ndeclare(strict_types=1);\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    public function up()\n    {\n        Schema::table('users', function (Blueprint $table) {\n            $table->string('auth0')->nullable();\n            $table->boolean('email_verified')->default(false);\n\n            $table->unique('auth0', 'users_auth0_unique');\n        });\n    }\n\n    public function down()\n    {\n        Schema::table('users', function (Blueprint $table) {\n            $table->dropUnique('users_auth0_unique');\n\n            $table->dropColumn('auth0');\n            $table->dropColumn('email_verified');\n        });\n    }\n};\n\n```\n\nNow run the migration:\n\n```bash\nphp artisan migrate\n```\n\n## Creating a User Repository\n\nYou'll need to create a new user repository class to handle the creation and retrieval of your Eloquent user models from your database table.\n\nCreate a new repository class in your application at `app/Repositories/UserRepository.php`, and update it to match the following example:\n\n```php\n<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Repositories;\n\nuse App\\Models\\User;\nuse Auth0\\Laravel\\{UserRepositoryAbstract, UserRepositoryContract};\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Support\\Facades\\Cache;\nuse Illuminate\\Support\\Facades\\Hash;\nuse Illuminate\\Support\\Str;\n\nfinal class UserRepository extends UserRepositoryAbstract implements UserRepositoryContract\n{\n    public function fromAccessToken(array $user): ?Authenticatable\n    {\n        /*\n            $user = [ // Example of a decoded access token\n                \"iss\"   => \"https://example.auth0.com/\",\n                \"aud\"   => \"https://api.example.com/calendar/v1/\",\n                \"sub\"   => \"auth0|123456\",\n                \"exp\"   => 1458872196,\n                \"iat\"   => 1458785796,\n                \"scope\" => \"read write\",\n            ];\n        */\n\n        $identifier = $user['sub'] ?? $user['auth0'] ?? null;\n\n        if (null === $identifier) {\n            return null;\n        }\n\n        return User::where('auth0', $identifier)->first();\n    }\n\n    public function fromSession(array $user): ?Authenticatable\n    {\n        /*\n            $user = [ // Example of a decoded ID token\n                \"iss\"         => \"http://example.auth0.com\",\n                \"aud\"         => \"client_id\",\n                \"sub\"         => \"auth0|123456\",\n                \"exp\"         => 1458872196,\n                \"iat\"         => 1458785796,\n                \"name\"        => \"Jane Doe\",\n                \"email\"       => \"janedoe@example.com\",\n            ];\n        */\n\n        // Determine the Auth0 identifier for the user from the $user array.\n        $identifier = $user['sub'] ?? $user['auth0'] ?? null;\n\n        // Collect relevant user profile information from the $user array for use later.\n        $profile = [\n            'auth0' => $identifier,\n            'name' => $user['name'] ?? '',\n            'email' => $user['email'] ?? '',\n            'email_verified' => in_array($user['email_verified'], [1, true], true),\n        ];\n\n        // Check if a cache of the user exists in memory to avoid unnecessary database queries.\n        $cached = $this->withoutRecording(fn () => Cache::get('auth0_user_' . $identifier));\n\n        if ($cached) {\n            // Immediately return a cached user if one exists.\n            return $cached;\n        }\n\n        $user = null;\n\n        // Check if the user exists in the database by Auth0 identifier.\n        if (null !== $identifier) {\n            $user = User::where('auth0', $identifier)->first();\n        }\n\n        // Optional: if the user does not exist in the database by Auth0 identifier, you could fallback to matching by email.\n        if (null === $user && isset($user['email'])) {\n            $user = User::where('email', $user['email'])->first();\n        }\n\n        // If a user was found, check if any updates to the local record are required.\n        if (null !== $user) {\n            $updates = [];\n\n            if ($user->auth0 !== $profile['auth0']) {\n                $updates['auth0'] = $profile['auth0'];\n            }\n\n            if ($user->name !== $profile['name']) {\n                $updates['name'] = $profile['name'];\n            }\n\n            if ($user->email !== $profile['email']) {\n                $updates['email'] = $profile['email'];\n            }\n\n            $emailVerified = in_array($user->email_verified, [1, true], true);\n\n            if ($emailVerified !== $profile['email_verified']) {\n                $updates['email_verified'] = $profile['email_verified'];\n            }\n\n            if ([] !== $updates) {\n                $user->update($updates);\n                $user->save();\n            }\n\n            if ([] === $updates && null !== $cached) {\n                return $user;\n            }\n        }\n\n        if (null === $user) {\n            // Local password column is not necessary or used by Auth0 authentication, but may be expected by some applications/packages.\n            $profile['password'] = Hash::make(Str::random(32));\n\n            // Create the user.\n            $user = User::create($profile);\n        }\n\n        // Cache the user for 30 seconds.\n        $this->withoutRecording(fn () => Cache::put('auth0_user_' . $identifier, $user, 30));\n\n        return $user;\n    }\n\n    /**\n     * Workaround for Laravel Telescope potentially causing an infinite loop.\n     * @link https://github.com/auth0/laravel-auth0/tree/main/docs/Telescope.md\n     *\n     * @param callable $callback\n     */\n    private function withoutRecording($callback): mixed\n    {\n        $telescope = '\\Laravel\\Telescope\\Telescope';\n\n        if (class_exists($telescope)) {\n            return \"$telescope\"::withoutRecording($callback);\n        }\n\n        return call_user_func($callback);\n    }\n}\n```\n\nFinally, update your application's `config/auth.php` file to configure the SDK to query your new user provider during authentication requests.\n\n```php\n'providers' => [\n  'auth0-provider' => [\n    'driver' => 'auth0.provider',\n    'repository' => \\App\\Repositories\\UserRepository::class,\n  ],\n],\n```\n"
  },
  {
    "path": "docs/Events.md",
    "content": "# Events\n\n- [Introduction](#introduction)\n- [SDK Controller Events](#sdk-controller-events)\n  - [Login Events](#login-events)\n  - [Callback Events](#callback-events)\n  - [Logout Events](#logout-events)\n- [Deprecated SDK Events](#deprecated-sdk-events)\n  - [Authentication Middleware Events](#authentication-middleware-events)\n  - [Authorization Middleware Events](#authorization-middleware-events)\n\n## Introduction\n\nYour application can listen to events raised by the SDK, and respond to them if desired. For example, you might want to log the user's information to a database when they log in.\n\nTo listen for these events, you must first create a listener class within your application. These usually live in your `app/Listeners` directory. The following example shows how to listen for the `Illuminate\\Auth\\Events\\Login` event:\n\n```php\nnamespace App\\Listeners;\n\nuse Illuminate\\Auth\\Events\\Login;\n\nfinal class LogSuccessfulLogin\n{\n    public function handle(Login $event): void\n    {\n        // Log the event to a database.\n    }\n}\n```\n\nYou should also register your event listeners in your application's `app/Providers/EventServiceProvider.php` file, for example:\n\n```php\nuse Illuminate\\Auth\\Events\\Login;\nuse App\\Listeners\\LogSuccessfulLogin;\nuse Illuminate\\Support\\Facades\\Event;\n\npublic function boot(): void\n{\n    Event::listen(\n        Login::class, // The event class.\n        [LogSuccessfulLogin::class, 'handle'] // Your listener class and method.\n    );\n}\n```\n\nYou can learn more about working with the Laravel event system in the [Laravel documentation](https://laravel.com/docs/events).\n\n## SDK Controller Events\n\n### Login Events\n\nDuring user authentication triggered by `Auth0\\Laravel\\Controllers\\LoginController` (the `/login` route, by default) the following events may be raised:\n\n| Event                                  | Description                                                                                  |\n| -------------------------------------- | -------------------------------------------------------------------------------------------- |\n| `Illuminate\\Auth\\Events\\Login`         | Raised when a user is logging in. The model of the user is provided with the event.          |\n| `Auth0\\Laravel\\Events\\LoginAttempting` | Raised before the login redirect is issued, allowing an opportunity to customize parameters. |\n\n### Callback Events\n\nDuring user authentication callback triggered by `Auth0\\Laravel\\Controllers\\CallbackController` (the `/callback` route, by default) the following events may be raised:\n\n| Event                                          | Description                                                                                                                                                                                                                                                               |\n| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `Illuminate\\Auth\\Events\\Attempting`            | Raised when a user is returned to the application after authenticating with Auth0. This is raised before verification of the authentication process begins.                                                                                                               |\n| `Illuminate\\Auth\\Events\\Failed`                | Raised when authentication with Auth0 failed. The reason is provided with the event as an array.                                                                                                                                                                          |\n| `Auth0\\Laravel\\Events\\AuthenticationFailed`    | Raised when authentication with Auth0 failed. This provides an opportunity to intercept the exception thrown by the middleware, by using the event's `setThrowException()` method to `false`. You can also customize the type of exception thrown using `setException()`. |\n| `Illuminate\\Auth\\Events\\Validated`             | Raised when authentication was successful, but immediately before the user's session is established.                                                                                                                                                                      |\n| `Auth0\\Laravel\\Events\\AuthenticationSucceeded` | Raised when authentication was successful. The model of the authenticated user is provided with the event.                                                                                                                                                                |\n\n### Logout Events\n\nDuring user logout by `Auth0\\Laravel\\Controllers\\LogoutController` (the `/logout` route, by default) the following events may be raised:\n\n| Event                           | Description                                                                          |\n| ------------------------------- | ------------------------------------------------------------------------------------ |\n| `Illuminate\\Auth\\Events\\Logout` | Raised when a user is logging out. The model of the user is provided with the event. |\n\n## Deprecated SDK Events\n\nThe following events are deprecated and will be removed in a future release. They are replaced by the events listed in the previous section.\n\n### Authentication Middleware Events\n\nDuring request handling with `Auth0\\Laravel\\Middleware\\AuthenticateMiddleware` or `Auth0\\Laravel\\Middleware\\AuthenticateOptionalMiddleware` the following events may be raised:\n\n| Event                                                       | Description                                                                        |\n| ----------------------------------------------------------- | ---------------------------------------------------------------------------------- |\n| `Auth0\\Laravel\\Events\\Middleware\\StatefulMiddlewareRequest` | Raised when a request is being handled by a session-based ('stateful') middleware. |\n\n### Authorization Middleware Events\n\nDuring request handling with `Auth0\\Laravel\\Middleware\\AuthorizeMiddleware` or `Auth0\\Laravel\\Middleware\\AuthorizeOptionalMiddleware` middleware, the following events may be raised:\n\n| Event                                                        | Description                                                                                                     |\n| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |\n| `Auth0\\Laravel\\Events\\Middleware\\StatelessMiddlewareRequest` | Raised when a request is being handled by an access token-based ('stateless') middleware.                       |\n| `Auth0\\Laravel\\Events\\TokenVerificationAttempting`           | Raised before an access token is attempted to be verified. The encoded token string is provided with the event. |\n| `Auth0\\Laravel\\Events\\TokenVerificationSucceeded`            | Raised when an access token is successfully verified. The decoded token contents are provided with the event.   |\n| `Auth0\\Laravel\\Events\\TokenVerificationFailed`               | Raised when an access token cannot be verified. The reason (as a string) is provided with the event.            |\n"
  },
  {
    "path": "docs/Installation.md",
    "content": "# Installation\n\n-   [Prerequisites](#prerequisites)\n-   [Install the SDK](#install-the-sdk)\n    -   [Using Quickstart (Recommended)](#using-quickstart-recommended)\n    -   [Installation with Composer](#installation-with-composer)\n        -   [Create a Laravel Application](#create-a-laravel-application)\n        -   [Install the SDK](#install-the-sdk-1)\n-   [Install the CLI](#install-the-cli)\n    -   [Authenticate the CLI](#authenticate-the-cli)\n-   [Configure the SDK](#configure-the-sdk)\n    -   [Using JSON (Recommended)](#using-json-recommended)\n    -   [Using Environment Variables](#using-environment-variables)\n\n## Prerequisites\n\nYour application must use the [latest supported Laravel version](https://endoflife.date/laravel), and your host environment must be running a [supported PHP version](https://www.php.net/supported-versions.php). Please review [our support policy](./docs/Support.md) for more information. You will also need [Composer 2.0+](https://getcomposer.org/) and an [Auth0 account](https://auth0.com/signup).\n\n## Install the SDK\n\nEnsure that your development environment has [supported versions](#prerequisites) of PHP and [Composer](https://getcomposer.org/) installed. If you're using macOS, PHP and Composer can be installed via [Homebrew](https://brew.sh/). It's also advisable to [install Node and NPM](https://nodejs.org/).\n\n### Using Quickstart (Recommended)\n\n-   Create a new Laravel 9 project pre-configured with the SDK:\n\n    ```shell\n    composer create-project auth0-samples/laravel auth0-laravel-app\n    ```\n\n### Installation with Composer\n\n#### Create a Laravel Application\n\n-   If you do not already have one, you can Create a new Laravel 9 application with the following command:\n\n    ```shell\n    composer create-project laravel/laravel:^9.0 auth0-laravel-app\n    ```\n\n#### Install the SDK\n\n1. Run the following command from your project directory to install the SDK:\n\n    ```shell\n    composer require auth0/login:^7.8 --update-with-all-dependencies\n    ```\n\n2. Generate an SDK configuration file for your application:\n\n    ```shell\n    php artisan vendor:publish --tag auth0\n    ```\n\n## Install the CLI\n\nInstall the [Auth0 CLI](https://github.com/auth0/auth0-cli) to create and manage Auth0 resources from the command line.\n\n-   macOS with [Homebrew](https://brew.sh/):\n\n    ```shell\n    brew tap auth0/auth0-cli && brew install auth0\n    ```\n\n-   Linux or macOS:\n\n    ```shell\n    curl -sSfL https://raw.githubusercontent.com/auth0/auth0-cli/main/install.sh | sh -s -- -b .\n    sudo mv ./auth0 /usr/local/bin\n    ```\n\n-   Windows with [Scoop](https://scoop.sh/):\n\n    ```cmd\n    scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git\n    scoop install auth0\n    ```\n\n### Authenticate the CLI\n\n-   Authenticate the CLI with your Auth0 account. Choose \"as a user,\" and follow the prompts.\n\n    ```shell\n    auth0 login\n    ```\n\n## Configure the SDK\n\n### Using JSON (Recommended)\n\n1. Register a new application with Auth0:\n\n    ```shell\n    auth0 apps create \\\n      --name \"My Laravel Application\" \\\n      --type \"regular\" \\\n      --auth-method \"post\" \\\n      --callbacks \"http://localhost:8000/callback\" \\\n      --logout-urls \"http://localhost:8000\" \\\n      --reveal-secrets \\\n      --no-input \\\n      --json > .auth0.app.json\n    ```\n\n2. Register a new API with Auth0:\n\n    ```shell\n    auth0 apis create \\\n      --name \"My Laravel Application API\" \\\n      --identifier \"https://github.com/auth0/laravel-auth0\" \\\n      --offline-access \\\n      --no-input \\\n      --json > .auth0.api.json\n    ```\n\n3. Add the new files to `.gitignore`:\n\n    Linux and macOS:\n\n    ```bash\n    echo \".auth0.*.json\" >> .gitignore\n    ```\n\n    Windows PowerShell:\n\n    ```powershell\n    Add-Content .gitignore \"`n.auth0.*.json\"\n    ```\n\n    Windows Command Prompt:\n\n    ```cmd\n    echo .auth0.*.json >> .gitignore\n    ```\n\n### Using Environment Variables\n\n1. Register a new application with Auth0:\n\n    ```shell\n    auth0 apps create \\\n      --name \"My Laravel Application\" \\\n      --type \"regular\" \\\n      --auth-method \"post\" \\\n      --callbacks \"http://localhost:8000/callback\" \\\n      --logout-urls \"http://localhost:8000\" \\\n      --reveal-secrets \\\n      --no-input\n    ```\n\n    Make a note of the `client_id` and `client_secret` values in the output.\n\n2. Register a new API with Auth0:\n\n    ```shell\n    auth0 apis create \\\n      --name \"My Laravel Application API\" \\\n      --identifier \"https://github.com/auth0/laravel-auth0\" \\\n      --offline-access \\\n      --no-input\n    ```\n\n3. Open the `.env` file found inside your project directory, and add the following lines, replacing the values with the ones you noted in the previous steps:\n\n    ```ini\n    # The Auth0 domain for your tenant (e.g. tenant.region.auth0.com):\n    AUTH0_DOMAIN=...\n\n    # The application `client_id` you noted above:\n    AUTH0_CLIENT_ID=...\n\n    # The application `client_secret` you noted above:\n    AUTH0_CLIENT_SECRET=...\n\n    # The API `identifier` you used above:\n    AUTH0_AUDIENCE=...\n    ```\n\n    Additional configuration environment variables can be found in the [configuration guide](./Configuration.md#environment-variables).\n"
  },
  {
    "path": "docs/Management.md",
    "content": "# Management API\n\nThe Auth0 Laravel SDK provides easy-to-use methods to access Auth0's Management API endpoints. Nearly every endpoint of the Management API is available to use with your Laravel application. For more information about any of these endpoints, see the [Management API Explorer](https://auth0.com/docs/api/management/v2).\n\nThe Management API class can be accessed through the `management()` method on the Auth0 Laravel SDK service. You can pull the Auth0 SDK instance from the Laravel service container using dependency injection, or use the `Auth0` facade. Once you have an instance, you can call any of the [available endpoints](#available-endpoints).\n\n```php\nuse Auth0\\Laravel\\Facade\\Auth0;\n\nAuth0::management();\n```\n\n## API Application Authorization\n\nBefore making Management API calls you must permit your application to communicate with the Management API. This can be done from the [Auth0 Dashboard's API page](https://manage.auth0.com/#/apis/), choosing `Auth0 Management API`, and selecting the 'Machine to Machine Applications' tab. Authorize your Laravel application, and then click the down arrow to choose the scopes you wish to grant.\n\n## Available endpoints\n\n- [Actions](#actions)\n- [Attack Protection](#attack-protection)\n- [Blacklists](#blacklists)\n- [ClientGrants](#client-grants)\n- [Clients](#clients)\n- [Connections](#connections)\n- [Device Credentials](#device-credentials)\n- [Emails](#emails)\n- [Email Templates](#email-templates)\n- [Grants](#grants)\n- [Guardian](#guardian)\n- [Jobs](#jobs)\n- [Logs](#logs)\n- [Log Streams](#log-streams)\n- [Organizations](#organizations)\n- [Resource Servers](#resource-servers)\n- [Roles](#roles)\n- [Rules](#rules)\n- [Stats](#stats)\n- [Tenants](#tenants)\n- [Tickets](#tickets)\n- [User Blocks](#user-blocks)\n- [Users](#users)\n- [Users by Email](#users-by-email)\n\n### Actions\n\nThe [/api/v2/actions](https://auth0.com/docs/api/management/v2#!/Actions) endpoint class is accessible from the `actions()` method on the Management API class.\n\n| Method   | Endpoint                                                                                                                          | SDK Method                                          |\n| -------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |\n| `GET`    | […/actions/actions](https://auth0.com/docs/api/management/v2#!/Actions/get_actions)                                               | `getAll()`                                          |\n| `POST`   | […/actions/actions](https://auth0.com/docs/api/management/v2#!/Actions/post_action)                                               | `create(body: [])`                                  |\n| `GET`    | […/actions/actions/{id}](https://auth0.com/docs/api/management/v2#!/Actions/get_action)                                           | `get(id: '...')`                                    |\n| `PATCH`  | […/actions/actions/{id}](https://auth0.com/docs/api/management/v2#!/Actions/patch_action)                                         | `update(id: '...', body: [])`                       |\n| `DELETE` | […/actions/actions/{id}](https://auth0.com/docs/api/management/v2#!/Actions/delete_action)                                        | `delete(id: '...')`                                 |\n| `POST`   | […/actions/actions/{id}/test](https://auth0.com/docs/api/management/v2#!/Actions/post_test_action)                                | `test(id: '...')`                                   |\n| `POST`   | […/actions/actions/{id}/deploy](https://auth0.com/docs/api/management/v2#!/Actions/post_deploy_action)                            | `deploy(id: '...')`                                 |\n| `GET`    | […/actions/actions/{actionId}/versions](https://auth0.com/docs/api/management/v2#!/Actions/get_action_versions)                   | `getVersions(actionId: '...')`                      |\n| `GET`    | […/actions/actions/{actionId}/versions/{id}](https://auth0.com/docs/api/management/v2#!/Actions/get_action_version)               | `getVersion(id: '...', actionId: '...')`            |\n| `POST`   | […/actions/actions/{actionId}/versions/{id}/deploy](https://auth0.com/docs/api/management/v2#!/Actions/post_deploy_draft_version) | `rollbackVersion(id: '...', actionId: '...')`       |\n| `GET`    | […/actions/executions/{id}](https://auth0.com/docs/api/management/v2#!/Actions/get_execution)                                     | `getExecution(id: '...')`                           |\n| `GET`    | […/actions/triggers](https://auth0.com/docs/api/management/v2#!/Actions/get_triggers)                                             | `getTriggers()`                                     |\n| `GET`    | […/actions/triggers/{triggerId}/bindings](https://auth0.com/docs/api/management/v2#!/Actions/get_bindings)                        | `getTriggerBindings(triggerId: '...')`              |\n| `PATCH`  | […/actions/triggers/{triggerId}/bindings](https://auth0.com/docs/api/management/v2#!/Actions/patch_bindings)                      | `updateTriggerBindings(triggerId: '...', body: [])` |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Actions class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Actions.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$actions = $management->actions();\n\n// Retrieves the first batch of results results.\n$results = $actions->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($actions->getResponsePaginator() as $action) {\n  // Do something with the action.\n}\n```\n\n### Attack Protection\n\nThe [/api/v2/attack-protection](https://auth0.com/docs/api/management/v2#!/Attack_Protection) endpoint class is accessible from the `attackProtection()` method on the Management API class.\n\n| Method  | Endpoint                                                                                                                                          | SDK Method                                  |\n| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |\n| `GET`   | […/attack-protection/breached-password-detection](https://auth0.com/docs/api/management/v2#!/Attack_Protection/get_breached_password_detection)   | `getBreachedPasswordDetection()`            |\n| `PATCH` | […/attack-protection/breached-password-detection](https://auth0.com/docs/api/management/v2#!/Attack_Protection/patch_breached_password_detection) | `updateBreachedPasswordDetection(body: [])` |\n| `GET`   | […/attack-protection/brute-force-protection](https://auth0.com/docs/api/management/v2#!/Attack_Protection/get_brute_force_protection)             | `getBruteForceProtection()`                 |\n| `PATCH` | […/attack-protection/brute-force-protection](https://auth0.com/docs/api/management/v2#!/Attack_Protection/patch_brute_force_protection)           | `updateBruteForceProtection(body: [])`      |\n| `GET`   | […/attack-protection/suspicious-ip-throttling](https://auth0.com/docs/api/management/v2#!/Attack_Protection/get_suspicious_ip_throttling)         | `getSuspiciousIpThrottling()`               |\n| `PATCH` | […/attack-protection/suspicious-ip-throttling](https://auth0.com/docs/api/management/v2#!/Attack_Protection/patch_suspicious_ip_throttling)       | `updateSuspiciousIpThrottling(body: [])`    |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\AttackProtection class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/AttackProtection.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$attackProtection = $management->attackProtection();\n\n// Get the current configuration.\n$response = $attackProtection->getBreachedPasswordDetection();\n\n// Print the response body.\ndd(HttpResponse::decode($response));\n\n// {\n//   \"enabled\": true,\n//   \"shields\": [\n//     \"block\",\n//     \"admin_notification\"\n//   ],\n//   \"admin_notification_frequency\": [\n//     \"immediately\",\n//     \"weekly\"\n//   ],\n//   \"method\": \"standard\",\n//   \"stage\": {\n//     \"pre-user-registration\": {\n//       \"shields\": [\n//         \"block\",\n//         \"admin_notification\"\n//       ]\n//     }\n//   }\n// }\n```\n\n### Blacklists\n\nThe [/api/v2/blacklists](https://auth0.com/docs/api/management/v2#!/Blacklists) endpoint class is accessible from the `blacklists()` method on the Management API class.\n\n| Method | Endpoint                                                                                 | SDK Method           |\n| ------ | ---------------------------------------------------------------------------------------- | -------------------- |\n| `GET`  | […/blacklists/tokens](https://auth0.com/docs/api/management/v2#!/Blacklists/get_tokens)  | `get()`              |\n| `POST` | […/blacklists/tokens](https://auth0.com/docs/api/management/v2#!/Blacklists/post_tokens) | `create(jti: '...')` |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Blacklists class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Blacklists.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$blacklists = $management->blacklists();\n\n$response = $blacklists->create('some-jti');\n\nif ($response->getStatusCode() === 201) {\n  // Token was successfully blacklisted.\n\n  // Retrieve all blacklisted tokens.\n  $results = $blacklists->get();\n\n  // You can then iterate (and auto-paginate) through all available results.\n  foreach ($blacklists->getResponsePaginator() as $blacklistedToken) {\n    // Do something with the blacklisted token.\n  }\n\n  // Or, just work with the initial batch from the response.\n  dd(HttpResponse::decode($results));\n\n  // [\n  //   {\n  //     \"aud\": \"...\",\n  //     \"jti\": \"some-jti\"\n  //   }\n  // ]\n}\n```\n\n### Client Grants\n\nThe [/api/v2/client-grants](https://auth0.com/docs/api/management/v2#!/Client_Grants) endpoint class is accessible from the `clientGrants()` method on the Management API class.\n\n| Method   | Endpoint                                                                                                    | SDK Method                                 |\n| -------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------ |\n| `GET`    | […/client-grants](https://auth0.com/docs/api/management/v2#!/Client_Grants/get_client_grants)               | `getAll()`                                 |\n| `GET`    | […/client-grants](https://auth0.com/docs/api/management/v2#!/Client_Grants/get_client_grants)               | `getAllByAudience(audience: '...')`        |\n| `GET`    | […/client-grants](https://auth0.com/docs/api/management/v2#!/Client_Grants/get_client_grants)               | `getAllByClientId(clientId: '...')`        |\n| `POST`   | […/client-grants](https://auth0.com/docs/api/management/v2#!/Client_Grants/post_client_grants)              | `create(clientId: '...', audience: '...')` |\n| `PATCH`  | […/client-grants/{id}](https://auth0.com/docs/api/management/v2#!/Client_Grants/patch_client_grants_by_id)  | `update(grantId: '...')`                   |\n| `DELETE` | […/client-grants/{id}](https://auth0.com/docs/api/management/v2#!/Client_Grants/delete_client_grants_by_id) | `delete(grantId: '...')`                   |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\ClientGrants class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/ClientGrants.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$clientGrants = $management->clientGrants();\n\n// Retrieves the first batch of results results.\n$results = $clientGrants->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($clientGrants->getResponsePaginator() as $clientGrant) {\n  // Do something with the client grant.\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"id\": \"\",\n//     \"client_id\": \"\",\n//     \"audience\": \"\",\n//     \"scope\": [\n//       \"\"\n//     ]\n//   }\n// ]\n```\n\n### Clients\n\nThe [/api/v2/clients](https://auth0.com/docs/api/management/v2#!/Clients) endpoint class is accessible from the `clients()` method on the Management API class.\n\n| Method   | Endpoint                                                                                  | SDK Method            |\n| -------- | ----------------------------------------------------------------------------------------- | --------------------- |\n| `GET`    | […/clients](https://auth0.com/docs/api/management/v2#!/Clients/get_clients)               | `getAll()`            |\n| `POST`   | […/clients](https://auth0.com/docs/api/management/v2#!/Clients/post_clients)              | `create(name: '...')` |\n| `GET`    | […/clients/{id}](https://auth0.com/docs/api/management/v2#!/Clients/get_clients_by_id)    | `get(id: '...')`      |\n| `PATCH`  | […/clients/{id}](https://auth0.com/docs/api/management/v2#!/Clients/patch_clients_by_id)  | `update(id: '...')`   |\n| `DELETE` | […/clients/{id}](https://auth0.com/docs/api/management/v2#!/Clients/delete_clients_by_id) | `delete(id: '...')`   |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Clients class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Clients.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$clients = $management->clients();\n\n// Retrieves the first batch of results results.\n$results = $clients->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($clients->getResponsePaginator() as $client) {\n  // Do something with the client.\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"client_id\": \"\",\n//     \"tenant\": \"\",\n//     \"name\": \"\",\n//     ...\n//   }\n// ]\n```\n\n### Connections\n\nThe [/api/v2/connections](https://auth0.com/docs/api/management/v2#!/Connections) endpoint class is accessible from the `connections()` method on the Management API class.\n\n| Method   | Endpoint                                                                                                 | SDK Method                             |\n| -------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------- |\n| `GET`    | […/connections](https://auth0.com/docs/api/management/v2#!/Connections/get_connections)                  | `getAll()`                             |\n| `POST`   | […/connections](https://auth0.com/docs/api/management/v2#!/Connections/post_connections)                 | `create(name: '...', strategy: '...')` |\n| `GET`    | […/connections/{id}](https://auth0.com/docs/api/management/v2#!/Connections/get_connections_by_id)       | `get(id: '...')`                       |\n| `PATCH`  | […/connections/{id}](https://auth0.com/docs/api/management/v2#!/Connections/patch_connections_by_id)     | `update(id: '...')`                    |\n| `DELETE` | […/connections/{id}](https://auth0.com/docs/api/management/v2#!/Connections/delete_connections_by_id)    | `delete(id: '...')`                    |\n| `DELETE` | […/connections/{id}/users](https://auth0.com/docs/api/management/v2#!/Connections/delete_users_by_email) | `deleteUser(id: '...', email: '...')`  |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Connections class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Connections.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$connections = $management->connections();\n\n// Retrieves the first batch of results results.\n$results = $connections->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($connections->getResponsePaginator() as $connection) {\n  // Do something with the connection.\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"name\": \"\",\n//     \"display_name\": \"\",\n//     \"options\": {},\n//     \"id\": \"\",\n//     \"strategy\": \"\",\n//     \"realms\": [\n//       \"\"\n//     ],\n//     \"is_domain_connection\": false,\n//     \"metadata\": {}\n//   }\n// ]\n```\n\n### Device Credentials\n\nThe [/api/v2/device-credentials](https://auth0.com/docs/api/management/v2#!/Device_Credentials) endpoint class is accessible from the `deviceCredentials()` method on the Management API class.\n\n| Method   | Endpoint                                                                                                                  | SDK Method                                                              |\n| -------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |\n| `GET`    | […/device-credentials](https://auth0.com/docs/api/management/v2#!/Device_Credentials/get_device_credentials)              | `get(userId: '...')`                                                    |\n| `POST`   | […/device-credentials](https://auth0.com/docs/api/management/v2#!/Device_Credentials/post_device_credentials)             | `create(deviceName: '...', type: '...', value: '...', deviceId: '...')` |\n| `DELETE` | […/device-credentials/{id}](https://auth0.com/docs/api/management/v2#!/Device_Credentials/delete_device_credential_by_id) | `delete(id: '...')`                                                     |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\DeviceCredentials class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/DeviceCredentials.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$deviceCredentials = $management->deviceCredentials();\n\n// Retrieves the first batch of results results.\n$results = $deviceCredentials->get('user_id');\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($deviceCredentials->getResponsePaginator() as $deviceCredential) {\n  // Do something with the device credential.\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"id\": \"\",\n//     \"device_name\": \"\",\n//     \"device_id\": \"\",\n//     \"type\": \"\",\n//     \"user_id\": \"\",\n//     \"client_id\": \"\"\n//   }\n// ]\n```\n\n### Emails\n\nThe [/api/v2/emails](https://auth0.com/docs/api/management/v2#!/Emails) endpoint class is accessible from the `emails()` method on the Management API class.\n\n| Method   | Endpoint                                                                               | SDK Method                                     |\n| -------- | -------------------------------------------------------------------------------------- | ---------------------------------------------- |\n| `GET`    | […/emails/provider](https://auth0.com/docs/api/management/v2#!/Emails/get_provider)    | `getProvider()`                                |\n| `POST`   | […/emails/provider](https://auth0.com/docs/api/management/v2#!/Emails/post_provider)   | `createProvider(name: '...', credentials: [])` |\n| `PATCH`  | […/emails/provider](https://auth0.com/docs/api/management/v2#!/Emails/patch_provider)  | `updateProvider(name: '...', credentials: [])` |\n| `DELETE` | […/emails/provider](https://auth0.com/docs/api/management/v2#!/Emails/delete_provider) | `deleteProvider()`                             |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Emails class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Emails.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$endpoint = $management->emails();\n\n// Configure the email provider.\n$endpoint->createProvider(\n  name: 'smtp',\n  credentials: [\n    'smtp_host' => '...',\n    'smtp_port' => 587,\n    'smtp_user' => '...',\n    'smtp_pass' => '...',\n  ],\n  body: [\n    'enabled' => true,\n    'default_from_address' => 'sender@auth0.com',\n  ]\n)\n\n// Retrieves the configuration of the email provider.\n$provider = $endpoint->getProvider();\n\n// Print the configuration.\ndd(HttpResponse::decode($provider));\n\n// {\n//   \"name\": \"smtp\",\n//   \"enabled\": true,\n//   \"default_from_address\": \"sender@auth0.com\",\n//   \"credentials\": {\n//     'smtp_host' => '...',\n//     'smtp_port' => 587,\n//     'smtp_user' => '...',\n//     'smtp_pass' => '...',\n//   },\n//   \"settings\": {}\n// }\n```\n\n### Email Templates\n\nThe [/api/v2/email-templates](https://auth0.com/docs/api/management/v2#!/Email_Templates) endpoint class is accessible from the `emailTemplates()` method on the Management API class.\n\n| Method  | Endpoint                                                                                                                             | SDK Method                                                                                        |\n| ------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |\n| `POST`  | […/email-templates](https://auth0.com/docs/api/management/v2#!/Email_Templates/post_email_templates)                                 | `create(template: '...', body: '...', from: '...', subject: '...', syntax: '...', enabled: true)` |\n| `GET`   | […/email-templates/{templateName}](https://auth0.com/docs/api/management/v2#!/Email_Templates/get_email_templates_by_templateName)   | `get(templateName: '...')`                                                                        |\n| `PATCH` | […/email-templates/{templateName}](https://auth0.com/docs/api/management/v2#!/Email_Templates/patch_email_templates_by_templateName) | `update(templateName: '...', body: [])`                                                           |\n| `PUT`   | […/email-templates/{templateName}](https://auth0.com/docs/api/management/v2#!/Email_Templates/put_email_templates_by_templateName)   | `update(templateName: '...', body: [])`                                                           |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\EmailTemplates class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/EmailTemplates.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$templates = $management->emailTemplates();\n\n// Create a new email template.\n$templates->create(\n  template: 'verify_email',\n  body: '...',\n  from: 'sender@auth0.com',\n  subject: '...',\n  syntax: 'liquid',\n  enabled: true,\n);\n\n// Retrieves the configuration of the email template.\n$template = $templates->get(templateName: 'verify_email');\n\n// Print the configuration.\ndd(HttpResponse::decode($template));\n\n// {\n//   \"template\": \"verify_email\",\n//   \"body\": \"\",\n//   \"from\": \"sender@auth0.com\",\n//   \"resultUrl\": \"\",\n//   \"subject\": \"\",\n//   \"syntax\": \"liquid\",\n//   \"urlLifetimeInSeconds\": 0,\n//   \"includeEmailInRedirect\": false,\n//   \"enabled\": false\n// }\n```\n\n### Grants\n\nThe [/api/v2/grants](https://auth0.com/docs/api/management/v2#!/Grants) endpoint class is accessible from the `grants()` method on the Management API class.\n\n| Method   | Endpoint                                                                               | SDK Method                          |\n| -------- | -------------------------------------------------------------------------------------- | ----------------------------------- |\n| `GET`    | […/grants](https://auth0.com/docs/api/management/v2#!/Grants/get_grants)               | `getAll()`                          |\n| `GET`    | […/grants](https://auth0.com/docs/api/management/v2#!/Grants/get_grants)               | `getAllByAudience(audience: '...')` |\n| `GET`    | […/grants](https://auth0.com/docs/api/management/v2#!/Grants/get_grants)               | `getAllByClientId(clientId: '...')` |\n| `GET`    | […/grants](https://auth0.com/docs/api/management/v2#!/Grants/get_grants)               | `getAllByUserId(userId: '...')`     |\n| `DELETE` | […/grants/{id}](https://auth0.com/docs/api/management/v2#!/Grants/delete_grants_by_id) | `delete(id: '...')`                 |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Grants class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Grants.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$grants = $management->grants();\n\n// Retrieves the first batch of grant results.\n$results = $grants->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($grants->getResponsePaginator() as $grant) {\n  // Do something with the device credential.\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"id\": \"...\",\n//     \"clientID\": \"...\",\n//     \"user_id\": \"...\",\n//     \"audience\": \"...\",\n//     \"scope\": [\n//       \"...\"\n//     ],\n//   }\n// ]\n```\n\n### Guardian\n\nThe [/api/v2/guardian](https://auth0.com/docs/api/management/v2#!/Guardian) endpoint class is accessible from the `guardian()` method on the Management API class.\n\n| Method   | Endpoint                                                                                                    | SDK Method                    |\n| -------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------- |\n| `GET`    | […/guardian/enrollments/{id}](https://auth0.com/docs/api/management/v2#!/Guardian/get_enrollments_by_id)    | `getEnrollment(id: '...')`    |\n| `DELETE` | […/guardian/enrollments/{id}](https://auth0.com/docs/api/management/v2#!/Guardian/delete_enrollments_by_id) | `deleteEnrollment(id: '...')` |\n| `GET`    | […/guardian/factors](https://auth0.com/docs/api/management/v2#!/Guardian/get_factors)                       | `getFactors()`                |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Guardian class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Guardian.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$guardian = $management->guardian();\n\n// Retrieves the first batch of factor results.\n$results = $guardian->getFactors();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($guardian->getResponsePaginator() as $factor) {\n  // Do something with the device credential.\n  dump($factor);\n\n  // {\n  //   \"enabled\": true,\n  //   \"trial_expired\": true,\n  //   \"name\": \"...\"\n  // }\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"enabled\": true,\n//     \"trial_expired\": true,\n//     \"name\": \"...\"\n//   }\n// ]\n```\n\n### Jobs\n\nThe [/api/v2/jobs](https://auth0.com/docs/api/management/v2#!/Jobs) endpoint class is accessible from the `jobs()` method on the Management API class.\n\n| Method | Endpoint                                                                                             | SDK Method                                                |\n| ------ | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |\n| `GET`  | […/jobs/{id}](https://auth0.com/docs/api/management/v2#!/Jobs/get_jobs_by_id)                        | `get(id: '...')`                                          |\n| `GET`  | […/jobs/{id}/errors](https://auth0.com/docs/api/management/v2#!/Jobs/get_errors)                     | `getErrors(id: '...')`                                    |\n| `POST` | […/jobs/users-exports](https://auth0.com/docs/api/management/v2#!/Jobs/post_users_exports)           | `createExportUsersJob(body: [])`                          |\n| `POST` | […/jobs/users-imports](https://auth0.com/docs/api/management/v2#!/Jobs/post_users_imports)           | `createImportUsers(filePath: '...', connectionId: '...')` |\n| `POST` | […/jobs/verification-email](https://auth0.com/docs/api/management/v2#!/Jobs/post_verification_email) | `createSendVerificationEmail(userId: '...')`              |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Jobs class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Jobs.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$connections = $management->connections();\n$jobs = $management->jobs();\n\n// Create a connection.\n$connection = $connections->create([\n  'name' => 'Test Connection',\n  'strategy' => 'auth0',\n]);\n\nif (! HttpResponse::wasSuccessful($job)) {\n  throw new \\Exception('Connection creation failed.');\n}\n\n$connection = HttpResponse::decode($connection);\n\n// Create a new user export job.\n$response = $jobs->createExportUsersJob([\n  'format' => 'json',\n  'fields' => [\n    ['name' => 'user_id'],\n    ['name' => 'name'],\n    ['name' => 'email'],\n    ['name' => 'identities[0].connection', \"export_as\": \"provider\"],\n    ['name' => 'user_metadata.some_field'],\n  ],\n  'connection_id' => $connection['id'],\n]);\n\nif ($response->getStatusCode() === 201) {\n  // The job was created successfully. Retrieve it's ID.\n  $jobId = HttpResponse::decode($response)['id'];\n  $job = null;\n\n  while (true) {\n    // Get the job status.\n    $job = $jobs->get($jobId);\n\n    if (! HttpResponse::wasSuccessful($job)) {\n      $job = null;\n      break;\n    }\n\n    $job = HttpResponse::decode($job);\n\n    // If the job is complete, break out of the loop.\n    if ($job['status'] === 'completed') {\n      break;\n    }\n\n    // If the job has failed, break out of the loop.\n    if ($job['status'] === 'failed') {\n      $job = null\n      break;\n    }\n\n    // Wait 1 second before checking the job status again.\n    sleep(1);\n  }\n\n  if ($job === null) {\n    // The job failed.\n    $errors = $jobs->getErrors($jobId);\n    dd($errors);\n  }\n\n  // The job completed successfully. Do something with the job.\n  dd($job);\n\n  // Delete the connection.\n  $connections->delete($connection['id']);\n}\n```\n\n### Logs\n\nThe [/api/v2/logs](https://auth0.com/docs/api/management/v2#!/Logs) endpoint class is accessible from the `logs()` method on the Management API class.\n\n| Method | Endpoint                                                                      | SDK Method       |\n| ------ | ----------------------------------------------------------------------------- | ---------------- |\n| `GET`  | […/logs](https://auth0.com/docs/api/management/v2#!/Logs/get_logs)            | `getAll()`       |\n| `GET`  | […/logs/{id}](https://auth0.com/docs/api/management/v2#!/Logs/get_logs_by_id) | `get(id: '...')` |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Logs class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Logs.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$logs = $management->logs();\n\n// Retrieves the first batch of log results.\n$results = $logs->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($logs->getResponsePaginator() as $log) {\n  // Do something with the log.\n  dump($log);\n\n  // {\n  //   \"date\": \"...\",\n  //   \"type\": \"...\",\n  //   \"description\": \"...\"\n  // }\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//   \"date\": \"...\",\n//   \"type\": \"...\",\n//   \"description\": \"...\"\n//   }\n// ]\n```\n\n### Log Streams\n\nThe [/api/v2/log-streams](https://auth0.com/docs/api/management/v2#!/Log_Streams) endpoint class is accessible from the `logStreams()` method on the Management API class.\n\n| Method   | Endpoint                                                                                              | SDK Method                         |\n| -------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------- |\n| `GET`    | […/log-streams](https://auth0.com/docs/api/management/v2#!/Log_Streams/get_log_streams)               | `getAll()`                         |\n| `POST`   | […/log-streams](https://auth0.com/docs/api/management/v2#!/Log_Streams/post_log_streams)              | `create(type: '...', sink: '...')` |\n| `GET`    | […/log-streams/{id}](https://auth0.com/docs/api/management/v2#!/Log_Streams/get_log_streams_by_id)    | `get(id: '...')`                   |\n| `PATCH`  | […/log-streams/{id}](https://auth0.com/docs/api/management/v2#!/Log_Streams/patch_log_streams_by_id)  | `update(id: '...', body: [])`      |\n| `DELETE` | […/log-streams/{id}](https://auth0.com/docs/api/management/v2#!/Log_Streams/delete_log_streams_by_id) | `delete(id: '...')`                |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\LogStreams class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/LogStreams.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$logStreams = $management->logStreams();\n\n// Create a new log stream.\n$logStreams->create(\n  type: '...',\n  sink: [\n    'name' => '...',\n  ]\n);\n\n// Get the first batch of log streams.\n$results = $logStreams->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($logStreams->getResponsePaginator() as $logStream) {\n  // Do something with the log stream.\n  dump($logStream);\n\n  // {\n  //   \"id\": \"...\",\n  //   \"name\": \"...\",\n  //   \"type\": \"...\",\n  //   \"status\": \"...\"\n  // }\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"id\": \"...\",\n//     \"name\": \"...\",\n//     \"type\": \"...\",\n//     \"status\": \"...\"\n//   }\n// ]\n```\n\n### Organizations\n\nThe [/api/v2/organizations](https://auth0.com/docs/api/management/v2#!/Organizations) endpoint class is accessible from the `organizations()` method on the Management API class.\n\n| Method   | Endpoint                                                                                                                                                       | SDK Method                                                                     |\n| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `GET`    | […/organizations](https://auth0.com/docs/api/management/v2#!/Organizations/get_organizations)                                                                  | `getAll()`                                                                     |\n| `POST`   | […/organizations](https://auth0.com/docs/api/management/v2#!/Organizations/post_organizations)                                                                 | `create(name: '...', displayName: '...')`                                      |\n| `GET`    | […/organizations/{id}](https://auth0.com/docs/api/management/v2#!/Organizations/get_organizations_by_id)                                                       | `get(id: '...')`                                                               |\n| `PATCH`  | […/organizations/{id}](https://auth0.com/docs/api/management/v2#!/Organizations/patch_organizations_by_id)                                                     | `update(id: '...', name: '...', displayName: '...')`                           |\n| `DELETE` | […/organizations/{id}](https://auth0.com/docs/api/management/v2#!/Organizations/delete_organizations_by_id)                                                    | `delete(id: '...')`                                                            |\n| `GET`    | […/organizations/name/{name}](https://auth0.com/docs/api/management/v2#!/Organizations/get_name_by_name)                                                       | `getByName(name: '...')`                                                       |\n| `GET`    | […/organizations/{id}/members](https://auth0.com/docs/api/management/v2#!/Organizations/get_members)                                                           | `getMembers(id: '...')`                                                        |\n| `POST`   | […/organizations/{id}/members](https://auth0.com/docs/api/management/v2#!/Organizations/post_members)                                                          | `addMembers(id: '...', members: [])`                                           |\n| `DELETE` | […/organizations/{id}/members](https://auth0.com/docs/api/management/v2#!/Organizations/delete_members)                                                        | `removeMembers(id: '...', members: [])`                                        |\n| `GET`    | […/organizations/{id}/invitations](https://auth0.com/docs/api/management/v2#!/Organizations/get_invitations)                                                   | `getInvitations(id: '...')`                                                    |\n| `POST`   | […/organizations/{id}/invitations](https://auth0.com/docs/api/management/v2#!/Organizations/post_invitations)                                                  | `createInvitation(id: '...', clientId: '...', inviter: '...', invitee: '...')` |\n| `GET`    | […/organizations/{id}/invitations/{invitationId}](https://auth0.com/docs/api/management/v2#!/Organizations/get_invitations_by_invitation_id)                   | `getInvitation(id: '...', invitationId: '...')`                                |\n| `DELETE` | […/organizations/{id}/invitations/{invitationId}](https://auth0.com/docs/api/management/v2#!/Organizations/delete_invitations_by_invitation_id)                | `deleteInvitation(id: '...', invitationId: '...')`                             |\n| `GET`    | […/organizations/{id}/enabled_connections](https://auth0.com/docs/api/management/v2#!/Organizations/get_enabled_connections)                                   | `getEnabledConnections(id: '...')`                                             |\n| `POST`   | […/organizations/{id}/enabled_connections](https://auth0.com/docs/api/management/v2#!/Organizations/post_enabled_connections)                                  | `addEnabledConnection(id: '...', connectionId: '...', body: [])`               |\n| `GET`    | […/organizations/{id}/enabled_connections/{connectionId}](https://auth0.com/docs/api/management/v2#!/Organizations/get_enabled_connections_by_connectionId)    | `getEnabledConnection(id: '...', connectionId: '...')`                         |\n| `PATCH`  | […/organizations/{id}/enabled_connections/{connectionId}](https://auth0.com/docs/api/management/v2#!/Organizations/patch_enabled_connections_by_connectionId)  | `updateEnabledConnection(id: '...' connectionId: '...', body: [])`             |\n| `DELETE` | […/organizations/{id}/enabled_connections/{connectionId}](https://auth0.com/docs/api/management/v2#!/Organizations/delete_enabled_connections_by_connectionId) | `removeEnabledConnection(id: '...', connectionId: '...')`                      |\n| `GET`    | […/organizations/{id}/members/{userId}/roles](https://auth0.com/docs/api/management/v2#!/Organizations/get_organization_member_roles)                          | `getMemberRoles(id: '...'. userId: '...')`                                     |\n| `POST`   | […/organizations/{id}/members/{userId}/roles](https://auth0.com/docs/api/management/v2#!/Organizations/post_organization_member_roles)                         | `addMemberRoles(id: '...'. userId: '...', roles: [])`                          |\n| `DELETE` | […/organizations/{id}/members/{userId}/roles](https://auth0.com/docs/api/management/v2#!/Organizations/delete_organization_member_roles)                       | `removeMemberRoles(id: '...'. userId: '...', roles: [])`                       |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Organizations class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Organizations.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$organizations = $management->organizations();\n\n// Get all organizations.\n$results = $organizations->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($logStreams->getResponsePaginator() as $logStream) {\n  // Do something with the log stream.\n  dump($logStream);\n\n  // {\n  //   \"id\": \"\",\n  //   \"name\": \"...\",\n  //   \"display_name\": \"...\",\n  // }\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"id\": \"\",\n//     \"name\": \"...\",\n//     \"display_name\": \"...\",\n// ]\n\n// Get a single organization.\n$results = $organizations->get('org_id');\n\n// Create a new organization.\n$results = $organizations->create('name', 'display_name');\n\n// Update an existing organization.\n$results = $organizations->update('org_id', 'name', 'display_name');\n\n// Delete an organization.\n$results = $organizations->delete('org_id');\n\n// Get all members of an organization.\n$results = $organizations->getMembers('org_id');\n\n// Add members to an organization.\n$results = $organizations->addMembers('org_id', ['user_id_1', 'user_id_2']);\n\n// Remove members from an organization.\n$results = $organizations->removeMembers('org_id', ['user_id_1', 'user_id_2']);\n\n// Get all invitations for an organization.\n$results = $organizations->getInvitations('org_id');\n\n// Create a new invitation for an organization.\n$results = $organizations->createInvitation('org_id', 'client_id', 'inviter_user_id', 'invitee_email');\n\n// Get a single invitation for an organization.\n$results = $organizations->getInvitation('org_id', 'invitation_id');\n\n// Delete an invitation for an organization.\n$results = $organizations->deleteInvitation('org_id', 'invitation_id');\n\n// Get all enabled connections for an organization.\n$results = $organizations->getEnabledConnections('org_id');\n\n// Add a connection to an organization.\n$results = $organizations->addEnabledConnection('org_id', 'connection_id', ['assign_membership_on_login' => true]);\n\n// Get a single enabled connection for an organization.\n$results = $organizations->getEnabledConnection('org_id', 'connection_id');\n\n// Update an enabled connection for an organization.\n$results = $organizations->updateEnabledConnection('org_id', 'connection_id', ['assign_membership_on_login' => false]);\n\n// Remove a connection from an organization.\n$results = $organizations->removeEnabledConnection('org_id', 'connection_id');\n\n// Get all roles for a member of an organization.\n$results = $organizations->getMemberRoles('org_id', 'user_id');\n\n// Add roles to a member of an organization.\n$results = $organizations->addMemberRoles('org_id', 'user_id', ['role_id_1', 'role_id_2']);\n\n// Remove roles from a member of an organization.\n$results = $organizations->removeMemberRoles('org_id', 'user_id', ['role_id_1', 'role_id_2']);\n```\n\n### Resource Servers\n\nThe [/api/v2/resource-servers](https://auth0.com/docs/api/management/v2#!/Resource_Servers) endpoint class is accessible from the `resourceServers()` method on the Management API class.\n\n| Method   | Endpoint                                                                                                             | PHP Method                            |\n| -------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |\n| `GET`    | […/resource-servers](https://auth0.com/docs/api/management/v2#!/Resource_Servers/get_resource_servers)               | `getAll()`                            |\n| `POST`   | […/resource-servers](https://auth0.com/docs/api/management/v2#!/Resource_Servers/post_resource_servers)              | `create(identifier: '...', body: [])` |\n| `GET`    | […/resource-servers/{id}](https://auth0.com/docs/api/management/v2#!/Resource_Servers/get_resource_servers_by_id)    | `get(id: '...')`                      |\n| `PATCH`  | […/resource-servers/{id}](https://auth0.com/docs/api/management/v2#!/Resource_Servers/patch_resource_servers_by_id)  | `update(id: '...', body: '...')`      |\n| `DELETE` | […/resource-servers/{id}](https://auth0.com/docs/api/management/v2#!/Resource_Servers/delete_resource_servers_by_id) | `delete(id: '...')`                   |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\ResourceServers class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/ResourceServers.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$resourceServers = $management->resourceServers();\n\n// Create a new resource server.\n$resourceServers->create(\n  identifier: 'https://my-resource-server.auth0.com',\n  body: [\n    'name' => 'My Example API',\n    'scopes' => [\n      [\n        'value' => 'read:messages',\n        'description' => 'Read messages',\n      ],\n      [\n        'value' => 'write:messages',\n        'description' => 'Write messages',\n      ],\n    ],\n  ]\n);\n\n// Get all resource servers.\n$results = $resourceServers->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($logStreams->getResponsePaginator() as $logStream) {\n  // Do something with the log stream.\n  dump($logStream);\n\n  // {\n  //   \"id\": \"\",\n  //   \"name\": \"\",\n  //   \"is_system\": false,\n  //   \"identifier\": \"\",\n  //   \"scopes\": [\n  //     \"object\"\n  //   ],\n  //   \"signing_alg\": \"\",\n  //   \"signing_secret\": \"\",\n  //   \"allow_offline_access\": false,\n  //   \"skip_consent_for_verifiable_first_party_clients\": false,\n  //   \"token_lifetime\": 0,\n  //   \"token_lifetime_for_web\": 0,\n  //   \"enforce_policies\": false,\n  //   \"token_dialect\": \"\",\n  //   \"client\": {}\n  // }\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"id\": \"\",\n//     \"name\": \"\",\n//     \"is_system\": false,\n//     \"identifier\": \"\",\n//     \"scopes\": [\n//       \"object\"\n//     ],\n//     \"signing_alg\": \"\",\n//     \"signing_secret\": \"\",\n//     \"allow_offline_access\": false,\n//     \"skip_consent_for_verifiable_first_party_clients\": false,\n//     \"token_lifetime\": 0,\n//     \"token_lifetime_for_web\": 0,\n//     \"enforce_policies\": false,\n//     \"token_dialect\": \"\",\n//     \"client\": {}\n//   }\n// ]\n```\n\n### Roles\n\nThe [/api/v2/roles](https://auth0.com/docs/api/management/v2#!/Roles) endpoint class is accessible from the `roles()` method on the Management API class.\n\n| Method   | Endpoint                                                                                                       | PHP Method                                      |\n| -------- | -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |\n| `GET`    | […/roles](https://auth0.com/docs/api/management/v2#!/Roles/get_roles)                                          | `getAll()`                                      |\n| `POST`   | […/roles](https://auth0.com/docs/api/management/v2#!/Roles/post_roles)                                         | `create(name: '...', body: [])`                 |\n| `GET`    | […/roles/{id}](https://auth0.com/docs/api/management/v2#!/Roles/get_roles_by_id)                               | `get(id: '...')`                                |\n| `PATCH`  | […/roles/{id}](https://auth0.com/docs/api/management/v2#!/Roles/patch_roles_by_id)                             | `update(id: '...', body: [])`                   |\n| `DELETE` | […/roles/{id}](https://auth0.com/docs/api/management/v2#!/Roles/delete_roles_by_id)                            | `delete(id: '...')`                             |\n| `GET`    | […/roles/{id}/users](https://auth0.com/docs/api/management/v2#!/Roles/get_role_user)                           | `getUsers(id: '...')`                           |\n| `POST`   | […/roles/{id}/users](https://auth0.com/docs/api/management/v2#!/Roles/post_role_users)                         | `addUsers(id: '...', users: [])`                |\n| `GET`    | […/roles/{id}/permissions](https://auth0.com/docs/api/management/v2#!/Roles/get_role_permission)               | `getPermissions(id: '...')`                     |\n| `POST`   | […/roles/{id}/permissions](https://auth0.com/docs/api/management/v2#!/Roles/post_role_permission_assignment)   | `addPermissions(id: '...', permissions: [])`    |\n| `DELETE` | […/roles/{id}/permissions](https://auth0.com/docs/api/management/v2#!/Roles/delete_role_permission_assignment) | `removePermissions(id: '...', permissions: [])` |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Roles class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Roles.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$roles = $management->roles();\n\n// Create a new role.\n$roles->create(\n  name: 'My Example Role',\n  body: [\n    'description' => 'This is an example role.',\n  ]\n);\n\n// Get all roles.\n$results = $roles->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($logStreams->getResponsePaginator() as $logStream) {\n  // Do something with the log stream.\n  dump($logStream);\n\n  // {\n  //   \"id\": \"\",\n  //   \"name\": \"\",\n  //   \"description\": \"\",\n  // }\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"id\": \"\",\n//     \"name\": \"\",\n//     \"description\": \"\",\n//   }\n// ]\n```\n\n### Rules\n\nThe [/api/v2/rules](https://auth0.com/docs/api/management/v2#!/Rules) endpoint class is accessible from the `rules()` method on the Management API class.\n\n| Method   | Endpoint                                                                            | PHP Method                           |\n| -------- | ----------------------------------------------------------------------------------- | ------------------------------------ |\n| `GET`    | […/rules](https://auth0.com/docs/api/management/v2#!/Rules/get_rules)               | `getAll()`                           |\n| `POST`   | […/rules](https://auth0.com/docs/api/management/v2#!/Rules/post_rules)              | `create(name: '...', script: '...')` |\n| `GET`    | […/rules/{id}](https://auth0.com/docs/api/management/v2#!/Rules/get_rules_by_id)    | `get(id: '...')`                     |\n| `PATCH`  | […/rules/{id}](https://auth0.com/docs/api/management/v2#!/Rules/patch_rules_by_id)  | `update(id: '...', body: [])`        |\n| `DELETE` | […/rules/{id}](https://auth0.com/docs/api/management/v2#!/Rules/delete_rules_by_id) | `delete(id: '...')`                  |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Rules class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Rules.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$rules = $management->rules();\n\n// Create a new rule.\n$rules->create(\n  name: 'My Example Rule',\n  script: 'function (user, context, callback) { callback(null, user, context); }'\n);\n\n// Get all rules.\n$results = $rules->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($logStreams->getResponsePaginator() as $logStream) {\n  // Do something with the log stream.\n  dump($logStream);\n\n  // {\n  //   \"id\": \"\",\n  //   \"name\": \"\",\n  //   \"script\": \"\",\n  //   \"enabled\": true,\n  //   \"order\": 0,\n  //   \"stage\": \"login_success\",\n  // }\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"id\": \"\",\n//     \"name\": \"\",\n//     \"script\": \"\",\n//     \"enabled\": true,\n//     \"order\": 0,\n//     \"stage\": \"login_success\",\n//   }\n// ]\n```\n\n### Stats\n\nThe [/api/v2/stats](https://auth0.com/docs/api/management/v2#!/Stats) endpoint class is accessible from the `stats()` method on the Management API class.\n\n| Method | Endpoint                                                                                  | PHP Method                        |\n| ------ | ----------------------------------------------------------------------------------------- | --------------------------------- |\n| `GET`  | […/stats/active-users](https://auth0.com/docs/api/management/v2#!/Stats/get_active_users) | `getActiveUsers()`                |\n| `GET`  | […/stats/daily](https://auth0.com/docs/api/management/v2#!/Stats/get_active_users)        | `getDaily(from: '...', to: '...)` |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Stats class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Stats.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$stats = $management->stats();\n\n// Retrieve the number of logins, signups and breached-password detections (subscription required) that occurred each day within a specified date range.\n$results = $stats->getDaily();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($stats->getResponsePaginator() as $metrics) {\n  // Do something with the log stream.\n  dump($logStream);\n\n  // {\n  //   \"date\": \"...\",\n  //   \"logins\": 0,\n  //   \"signups\": 0,\n  //   \"leaked_passwords\": 0,\n  //   \"updated_at\": \"...\",\n  //   \"created_at\": \"...\"\n  // }\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"date\": \"...\",\n//     \"logins\": 0,\n//     \"signups\": 0,\n//     \"leaked_passwords\": 0,\n//     \"updated_at\": \"...\",\n//     \"created_at\": \"...\"\n//   }\n// ]\n```\n\n### Tenants\n\nThe [/api/v2/tenants](https://auth0.com/docs/api/management/v2#!/Tenants) endpoint class is accessible from the `tenants()` method on the Management API class.\n\n| Method  | Endpoint                                                                                | PHP Method                 |\n| ------- | --------------------------------------------------------------------------------------- | -------------------------- |\n| `GET`   | […/tenants/settings](https://auth0.com/docs/api/management/v2#!/Tenants/get_settings)   | `getSettings()`            |\n| `PATCH` | […/tenants/settings](https://auth0.com/docs/api/management/v2#!/Tenants/patch_settings) | `updateSettings(body: [])` |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Tenants class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Tenants.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$tenants = $management->tenants();\n\n// Retrieve the current tenant settings.\n$results = $tenants->getSettings();\n\ndd(HttpResponse::decode($results));\n\n// {\n//   \"change_password\": {\n//     \"enabled\": false,\n//     \"html\": \"\"\n//   },\n//   ...\n//   ...\n//   ...\n// }\n```\n\n### Tickets\n\nThe [/api/v2/tickets](https://auth0.com/docs/api/management/v2#!/Tickets) endpoint class is accessible from the `tickets()` method on the Management API class.\n\n| Method | Endpoint                                                                                                   | PHP Method                               |\n| ------ | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------- |\n| `POST` | […/tickets/password-change](https://auth0.com/docs/api/management/v2#!/Tickets/post_password_change)       | `createPasswordChange(body: [])`         |\n| `POST` | […/tickets/email-verification](https://auth0.com/docs/api/management/v2#!/Tickets/post_email_verification) | `createEmailVerification(userId: '...')` |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\Tickets class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/Tickets.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$tickets = $management->tickets();\n\n// Create a password change ticket.\n$results = $tickets->createPasswordChange([\n  'result_url' => 'https://example.com',\n  'user_id' => '...',\n  'client_id' => '...',\n  'organization_id' => '...',\n  'connection_id' => '...',\n  'email' => '...',\n  'ttl_sec' => 3600,\n  'mark_email_as_verified' => true,\n  'includeEmailInRedirect' => true,\n]);\n\ndd(HttpResponse::decode($results));\n\n// {\n//   \"ticket\": \"https://login.auth0.com/lo/reset?...\"\n// }\n```\n\n### User Blocks\n\nThe [/api/v2/user-blocks](https://auth0.com/docs/api/management/v2#!/User_Blocks) endpoint class is accessible from the `userBlocks()` method on the Management API class.\n\n| Method   | Endpoint                                                                                              | PHP Method                              |\n| -------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------- |\n| `GET`    | […/user-blocks](https://auth0.com/docs/api/management/v2#!/User_Blocks/get_user_blocks)               | `get(id: '...')`                        |\n| `DELETE` | […/user-blocks](https://auth0.com/docs/api/management/v2#!/User_Blocks/delete_user_blocks)            | `delete(id: '...')`                     |\n| `GET`    | […/user-blocks/{id}](https://auth0.com/docs/api/management/v2#!/User_Blocks/get_user_blocks_by_id)    | `getByIdentifier(identifier: '...')`    |\n| `DELETE` | […/user-blocks/{id}](https://auth0.com/docs/api/management/v2#!/User_Blocks/delete_user_blocks_by_id) | `deleteByIdentifier(identifier: '...')` |\n\nFor full usage reference of the available API methods please [review the Auth0\\SDK\\API\\Management\\UserBlocks class.](https://github.com/auth0/auth0-PHP/blob/main/src/API/Management/UserBlocks.php)\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$userBlocks = $management->userBlocks();\n\n// Retrieve a list of all user blocks.\n$results = $userBlocks->get('...');\n\ndd(HttpResponse::decode($results));\n\n// {\n//   \"blocked_for\": [\n//     {\n//       \"identifier\": \"...\",\n//       \"ip\": \"...\"\n//     }\n//   ]\n// }\n```\n\n### Users\n\nThe [/api/v2/users](https://auth0.com/docs/api/management/v2#!/Users) endpoint class is accessible from the `users()` method on the Management API class.\n\n| Method   | Endpoint                                                                                                                                                   | PHP Method                                                       |\n| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |\n| `GET`    | […/users](https://auth0.com/docs/api/management/v2#!/Users/get_users)                                                                                      | `getAll()`                                                       |\n| `POST`   | […/users](https://auth0.com/docs/api/management/v2#!/Users/post_users)                                                                                     | `create(connection: '...', body: [])`                            |\n| `GET`    | […/users/{id}](https://auth0.com/docs/api/management/v2#!/Users/get_users_by_id)                                                                           | `get(id: '...')`                                                 |\n| `PATCH`  | […/users/{id}](https://auth0.com/docs/api/management/v2#!/Users/patch_users_by_id)                                                                         | `update(id: '...', body: [])`                                    |\n| `DELETE` | […/users/{id}](https://auth0.com/docs/api/management/v2#!/Users/delete_users_by_id)                                                                        | `delete(id: '...')`                                              |\n| `GET`    | […/users/{id}/enrollments](https://auth0.com/docs/api/management/v2#!/Users/get_enrollments)                                                               | `getEnrollments(id: '...')`                                      |\n| `GET`    | […/users/{user}/authentication-methods](https://auth0.com/docs/api/management/v2#!/Users/get_authentication_methods)                                       | `getAuthenticationMethods(user: '...')`                          |\n| `DELETE` | […/users/{user}/authentication-methods](https://auth0.com/docs/api/management/v2#!/Users/delete_authentication_methods)                                    | `deleteAuthenticationMethods(user: '...')`                       |\n| `POST`   | […/users/{user}/authentication-methods](https://auth0.com/docs/api/management/v2#!/Users/post_authentication_methods)                                      | `createAuthenticationMethod(user: '...', body: [])`              |\n| `GET`    | […/users/{id}/authentication-methods/{method}](https://auth0.com/docs/api/management/v2#!/Users/get_authentication_methods_by_authentication_method_id)    | `getAuthenticationMethod(id: '...', method: '...')`              |\n| `PATCH`  | […/users/{id}/authentication-methods/{method}](https://auth0.com/docs/api/management/v2#!/Users/patch_authentication_methods_by_authentication_method_id)  | `updateAuthenticationMethod(id: '...', method: '...', body: [])` |\n| `DELETE` | […/users/{id}/authentication-methods/{method}](https://auth0.com/docs/api/management/v2#!/Users/delete_authentication_methods_by_authentication_method_id) | `deleteAuthenticationMethod(id: '...', method: '...')`           |\n| `GET`    | […/users/{id}/organizations](https://auth0.com/docs/api/management/v2#!/Users/get_user_organizations)                                                      | `getOrganizations(id: '...')`                                    |\n| `GET`    | […/users/{id}/logs](https://auth0.com/docs/api/management/v2#!/Users/get_logs_by_user)                                                                     | `getLogs(id: '...')`                                             |\n| `GET`    | […/users/{id}/roles](https://auth0.com/docs/api/management/v2#!/Users/get_user_roles)                                                                      | `getRoles(id: '...')`                                            |\n| `POST`   | […/users/{id}/roles](https://auth0.com/docs/api/management/v2#!/Users/post_user_roles)                                                                     | `addRoles(id: '...', roles: [])`                                 |\n| `DELETE` | […/users/{id}/roles](https://auth0.com/docs/api/management/v2#!/Users/delete_user_roles)                                                                   | `removeRoles(id: '...', roles: [])`                              |\n| `GET`    | […/users/{id}/permissions](https://auth0.com/docs/api/management/v2#!/Users/get_permissions)                                                               | `getPermissions(id: '...')`                                      |\n| `POST`   | […/users/{id}/permissions](https://auth0.com/docs/api/management/v2#!/Users/post_permissions)                                                              | `addPermissions(id: '...', permissions: [])`                     |\n| `DELETE` | […/users/{id}/permissions](https://auth0.com/docs/api/management/v2#!/Users/delete_permissions)                                                            | `removePermissions(id: '...', permissions: [])`                  |\n| `DELETE` | […/users/{id}/multifactor/{provider}](https://auth0.com/docs/api/management/v2#!/Users/delete_multifactor_by_provider)                                     | `deleteMultifactorProvider(id: '...', provider: '...')`          |\n| `POST`   | […/users/{id}/identities](https://auth0.com/docs/api/management/v2#!/Users/post_identities)                                                                | `linkAccount(id: '...', body: [])`                               |\n| `DELETE` | […/users/{id}/identities/{provider}/{identityId}](https://auth0.com/docs/api/management/v2#!/Users/delete_provider_by_user_id)                             | `unlinkAccount(id: '...', provider: '...', identityId: '...')`   |\n| `POST`   | […/users/{id}/recovery-code-regeneration](https://auth0.com/docs/api/management/v2#!/Users/post_recovery_code_regeneration)                                | `createRecoveryCode(id: '...')`                                  |\n| `POST`   | […/users/{id}/multifactor/actions/invalidate-remember-browser](https://auth0.com/docs/api/management/v2#!/Users/post_invalidate_remember_browser)          | `invalidateBrowsers(id: '...')`                                  |\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$users = $management->users();\n\n// Create a new user.\n$users->create(\n  connection: 'Username-Password-Authentication',\n  body: [\n    'email' => '...',\n    'password' => '...',\n    'email_verified' => true,\n  ]\n);\n\n// Get a single user.\n$result = $users->get('auth0|...');\n\ndump(HttpResponse::decodedBody($result));\n\n// Get all users.\n$results = $users->getAll();\n\n// You can then iterate (and auto-paginate) through all available results.\nforeach ($users->getResponsePaginator() as $user) {\n  dump($user);\n\n// {\n//   \"user_id\": \"...\",\n//   \"email\": \"...\",\n//   \"email_verified\": true,\n//   ...\n// }\n}\n\n// Or, just work with the initial batch from the response.\ndd(HttpResponse::decode($results));\n\n// [\n//   {\n//     \"user_id\": \"...\",\n//     \"email\": \"...\",\n//     \"email_verified\": true,\n//     ...\n//   }\n// ]\n```\n\n# Users by Email\n\nThe `Auth0\\SDK\\API\\Management\\UsersByEmail` class provides methods to access the [Users by Email endpoint](https://auth0.com/docs/api/management/v2#!/Users_By_Email) of the v2 Management API.\n\n| Method | Endpoint                                                                                         | PHP Method |\n| ------ | ------------------------------------------------------------------------------------------------ | ---------- |\n| `GET`  | […/users-by-email](https://auth0.com/docs/api/management/v2#!/Users_By_Email/get_users_by_email) | `get()`    |\n\n```php\nuse Auth0\\SDK\\Utility\\HttpResponse;\n\n$management = app('auth0')->management();\n$usersByEmail = $management->usersByEmail();\n\n// Get a single user by email.\n$result = $usersByEmail->get('...');\n\ndump(HttpResponse::decodedBody($result));\n\n// {\n//   \"user_id\": \"...\",\n//   \"email\": \"...\",\n//   \"email_verified\": true,\n//   ...\n// }\n```\n"
  },
  {
    "path": "docs/Octane.md",
    "content": "# Octane Support\n\nOctane compatibility with the SDK is currently considered experimental and is not supported.\n\nAlthough we are working toward ensuring the SDK has full compatibility in the future, we do not recommend using this with our SDK in production until we have full confidence and announced support. There is an opportunity for problems we have not fully identified or addressed yet.\n\nFeedback and bug-fix contributions are greatly appreciated as we work toward full support.\n"
  },
  {
    "path": "docs/Sessions.md",
    "content": "# Sessions\n\nIn order to persist users' authentication states between HTTP requests, the Auth0 Laravel SDK uses Laravel's Session API to store and retrieve necessary data about the user. Applications can configure Laravel's Session API by modifying their `config/session.php` file. By default, sessions use the `file` driver, which stores the serialized user information in a file on the application server. However, you can configure the session store to use any of the other session drivers, such as `cookie`, `database`, `apc`, `memcached` and `redis`.\n\nIt's important to note that all session drivers, except for `cookie`, require server-side storage of the session data. If you are using a load balancer or other server cluster, you must use a session driver that is shared across all of the servers.\n\nWe strongly recommend using the `database` or `redis` session drivers for applications that use Auth0. These drivers are the most reliable and scalable options for storing session data.\n\n## Files\n\nThe default session driver is `file`, which stores the session data in files on the server. It works well for simple applications, but it does not scale reliably beyond a single server.\n\n## Cookies\n\nThe `cookie` session driver stores the session data in secure, encrypted cookies on the client device. Although convenient, this approach is not a reliable option for production applications as it suffers from a number of notable drawbacks:\n\n- Browsers impose a size limit of 4 KB on individual cookies, which can quickly be exceeded by storing session data.\n- Laravel's cookie driver unfortunately does not \"chunk\" (split up) larger cookies into multiple cookies, so it is impossible to store more than the noted 4 KB of total session data.\n- Most web servers and load balancers require additional configuration to accept and deliver larger cookie headers.\n\nIf your application requires the use of cookies, please use the Auth0 PHP SDK's custom cookie session handler instead. This approach supports chunking of larger cookies, but is notably incompatible with [Octane](./Octane.md). Please refer to [Cookies.md](./Cookies.md) for more information.\n\n## Database\n\nThe `database` session driver stores the session data in a database table. This is a very reliable option for applications of any size, but it does require a database connection to be configured for your application.\n\n## Redis\n\nThe `redis` session driver stores the session data in a Redis database. This is an equally reliable option to the `database` driver.\n\n## APC\n\nThe `apc` session driver stores the session data in the APC cache. This is a very fast and reliable option for applications of any size, but it does require the APC PHP extension to be installed on your server.\n\n## Memcached\n\nThe `memcached` session driver stores the session data in a Memcached database. This is an equally reliable option to the `apc` driver, but it does require the Memcached PHP extension to be installed on your server.\n\n## Array (Testing)\n\nThe `array` session driver stores the session data in a PHP array. This option is generally used for running tests on your application as it does not persist session data between requests.\n"
  },
  {
    "path": "docs/Support.md",
    "content": "# Support\n\nYour application must use a [supported Laravel version](#supported-laravel-releases), and your host environment must be running a [maintained PHP version](https://www.php.net/supported-versions.php).\n\nYou will also need [Composer](https://getcomposer.org/) and an [Auth0 account](https://auth0.com/signup).\n\n### Supported Laravel Releases\n\n| Laravel                                        | SDK    | PHP                                            | Supported Until                                                                                  |\n| ---------------------------------------------- | ------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------ |\n| [13.x](https://laravel.com/docs/13.x/releases) | 7.21+  | [8.4](https://www.php.net/releases/8.4/en.php) | Approx. [Dec 2028](https://www.php.net/supported-versions.php) (EOL for PHP 8.4)                 |\n|                                                |        | [8.3](https://www.php.net/releases/8.3/en.php) | Approx. [March 2028](https://laravel.com/docs/13.x/releases#support-policy) (EOL for Laravel 13) |\n| [12.x](https://laravel.com/docs/12.x/releases) | 7.16+  | [8.4](https://www.php.net/releases/8.4/en.php) | Approx. [Dec 2028](https://www.php.net/supported-versions.php) (EOL for PHP 8.4)                 |\n|                                                |        | [8.3](https://www.php.net/releases/8.3/en.php) | Approx. [Feb 2027](https://laravel.com/docs/12.x/releases#support-policy) (EOL for Laravel 12)   |\n|                                                |        | [8.2](https://www.php.net/releases/8.2/en.php) | Approx. [Dec 2026](https://www.php.net/supported-versions.php) (EOL for PHP 8.2)                 |\n| [11.x](https://laravel.com/docs/11.x/releases) | 7.13+  | [8.3](https://www.php.net/releases/8.3/en.php) | Approx. [March 2026](https://laravel.com/docs/11.x/releases#support-policy) (EOL for Laravel 11) |\n|                                                |        | [8.2](https://www.php.net/releases/8.2/en.php) | Approx. [Dec 2026](https://www.php.net/supported-versions.php) (EOL for PHP 8.2)                 |\n\nWe strive to support all actively maintained Laravel releases, prioritizing support for the latest major version with our SDK. If a new Laravel major introduces breaking changes, we may have to end support for past Laravel versions earlier than planned.\n\nAffected Laravel versions will still receive security fixes until their end-of-life date, as announced in our release notes.\n\n### Maintenance Releases\n\nThe following releases are no longer being updated with new features by Auth0, but will continue to receive security updates through their end-of-life date.\n\n| Laravel                                        | SDK        | PHP                                            | Security Fixes Until                                                                   |\n| ---------------------------------------------- | ---------- | ---------------------------------------------- | -------------------------------------------------------------------------------------- |\n| [10.x](https://laravel.com/docs/10.x/releases) | 7.5 - 7.12 | [8.3](https://www.php.net/releases/8.3/en.php) | [Feb 2025](https://laravel.com/docs/10.x/releases#support-policy) (EOL for Laravel 10) |\n|                                                |            | [8.2](https://www.php.net/releases/8.2/en.php) | [Feb 2025](https://laravel.com/docs/10.x/releases#support-policy) (EOL for Laravel 10) |\n|                                                |            | [8.1](https://www.php.net/releases/8.2/en.php) | [Nov 2024](https://www.php.net/supported-versions.php) (EOL for PHP 8.1)               |\n\n### Unsupported Releases\n\nThe following releases are unsupported by Auth0. While they may be suitable for some legacy applications, your mileage may vary. We recommend upgrading to a supported version as soon as possible.\n\n| Laravel                                      | SDK        |\n| -------------------------------------------- | ---------- |\n| [9.x](https://laravel.com/docs/9.x/releases) | 7.0 - 7.12 |\n| [8.x](https://laravel.com/docs/8.x/releases) | 7.0 - 7.4  |\n| [7.x](https://laravel.com/docs/7.x/releases) | 5.4 - 6.5  |\n| [6.x](https://laravel.com/docs/6.x/releases) | 5.3 - 6.5  |\n| [5.x](https://laravel.com/docs/5.x/releases) | 2.0 - 6.1  |\n| [4.x](https://laravel.com/docs/4.x/releases) | 1.x        |\n\n## Support Policy\n\nThe SDK follows the [Laravel support policy](https://laravel.com/docs/master/releases#support-policy) and will be supported until the Laravel version it supports reaches end-of-life, or it is no longer technically feasible to support.\n\n## Getting Support\n\n-   If you believe you've found a bug, please [create an issue on GitHub](https://github.com/auth0/laravel-auth0).\n-   For questions and community support, please [join the Auth0 Community](https://community.auth0.com/).\n-   For paid support plans, please [contact us directly](https://auth0.com/contact-us).\n-   For more information about Auth0 Support, please visit our [Support Center](https://support.auth0.com/).\n"
  },
  {
    "path": "docs/Telescope.md",
    "content": "# Laravel Telescope\n\nAs of 7.11.0, the Auth0 Laravel SDK is compatible with Laravel's Telescope debugging package. However, there are some caveats to be aware of when using the two together.\n\n## Cause of Potential Issues\n\nIssues stem from the fact that Telescope attempts to attribute events it's recording to the authenticated user. While this is useful information to log, it presents a problem. Because Telescope hooks into a number number of events (including the cache, queries, and events system) that the SDK raises during its authentication resolution process, this can cause an infinite loop.\n\nWhen a request to your application occurs, the SDK works to determine if the end user is authenticated. It executes a number of authenticated related events that Telescope happens to record by default. When these events are recorded by Telescope it asks the authentication API to determine if the end user is authenticated, which in turn calls the SDK to determine if the end user is authenticated, and thus the loop begins.\n\n7.11.0 introduced special checks for when Telescope is installed to prevent this from occurring, but it may not cover all cases.\n\nIf you are encountering Telescope causing infinite loops, you may need to disable the offending watchers in your `config/telescope.php` file. Alternatively, you can try wrapping any problematic code in Telescope's `withoutRecording()` method to prevent it from being recorded by Telescope. For example:\n\n```php\n\\Laravel\\Telescope\\Telescope::withoutRecording(function () {\n    // Your code here...\n});\n```\n\n## Missing Authentication Information from Telescope\n\nA side effect of the workarounds introduced in 7.11.0 that prevent Telescope from causing infinite loops is that Telescope may be unable to attribute recorded events triggered by the SDK to the authenticated user. This is intentional and necessary, and not a bug.\n\n## SDK <7.11.0 Workarounds\n\nIn versions prior to 7.11.0, you may encounter a compatibility issue with the SDK and Telescope when installed and enabled together. You may need to disable offending watchers in your `config/telescope.php` file to resolve this.\n\nFor example, if you are encountering issues with Telescope's `EventWatcher`, you can disable it in your `config/telescope.php` file, or ignore specific SDK events that are causing the issue. For example:\n\n```php\n<?php\n\nuse Laravel\\Telescope\\Http\\Middleware\\Authorize;\nuse Laravel\\Telescope\\Watchers;\n\nreturn [\n    'watchers' => [\n        Watchers\\EventWatcher::class => [\n            'enabled' => env('TELESCOPE_EVENT_WATCHER', true),\n            'ignore' => [\n                \\Auth0\\Laravel\\Events\\Configuration\\BuiltConfigurationEvent::class,\n                \\Auth0\\Laravel\\Events\\Configuration\\BuildingConfigurationEvent::class,\n            ],\n        ],\n    ],\n\n    // Other configuration options left out for brevity...\n];\n```\n"
  },
  {
    "path": "docs/Users.md",
    "content": "# Users\n\n- [User Persistenece](#user-persistenece)\n- [Best Practices](#best-practices)\n- [Retrieving User Information](#retrieving-user-information)\n- [Updating User Information](#updating-user-information)\n- [Extending the SDK](#extending-the-sdk)\n  - [User Repositories](#user-repositories)\n  - [Eloquent User Models](#eloquent-user-models)\n\n## User Persistence\n\nBy default the SDK does not persist user information to a database.\n\n- When a user authenticates with your application, the SDK retrieves their profile data from Auth0 and stores it within their session.\n- During each subsequent request, the SDK retrieves the stored profile data from the session and constructs a model representing the authenticated user from it.\n- This user model is available to your application via the `Auth` facade or `auth()` helper for the duration of the current request.\n\nLater in this guide we'll demonstrate how you can extend this default behavior to persist that profile data to your application's database, if desired.\n\n## Best Practices\n\nAuth0 provides a number of features that can simplify your application's authentication and authorization workflows. It may be helpful to keep the following best practices in mind as you integrate the SDK into your application:\n\n- Treat Auth0 as the single source of truth about your users.\n- If you must store user information in a database, store as little as possible. Treat any stored data as a cache, and sync it regularly using [the Management API](./Management.md).\n- Always use the [the Management API](./Management.md) to update user information. If you're storing user information in a database, sync those changes to your database as needed, not the other way around.\n\n## Retrieving User Information\n\nTo retrieve information about the currently authenticated user, use the `user()` method on the `Auth` facade or `auth()` helper.\n\n```php\nauth()->user();\n```\n\nYou can also retrieve information on any user using [the Management API](./Management.md). This also returns extended information not usually contained in the session state, such as user metadata.\n\n```php\nuse Auth0\\Laravel\\Facade\\Auth0;\n\nRoute::get('/profile', function () {\n  $profile = Auth0::management()->users()->get(auth()->id());\n  $profile = Auth0::json($profile);\n\n  $name = $profile['name'] ?? 'Unknown';\n  $email = $profile['email'] ?? 'Unknown';\n\n  return response(\"Hello {$name}! Your email address is {$email}.\");\n})->middleware('auth');\n```\n\n## Updating User Information\n\nTo update a user's information, use [the Management API](./Management.md).\n\n```php\nuse Auth0\\Laravel\\Facade\\Auth0;\n\nRoute::get('/update', function () {\n  Auth0::management()\n    ->users()\n    ->update(\n        id: auth()->id(),\n        body: [\n            'user_metadata' => [\n                'last_visited' => time()\n            ]\n        ]\n    );\n})->middleware('auth');\n```\n\n## Extending the SDK\n\n### User Repositories\n\nBy default the SDK does not store user information in your application's database. Instead, it uses the session to store the user's ID token, and retrieves user information from the token when needed. This is a good default behavior, but it may not be suitable for all applications.\n\nThe SDK uses a repository pattern to allow you to customize how user information is stored and retrieved. This allows you to use your own database to cache user information between authentication requests, or to use a different storage mechanism entirely.\n\n#### Creating a User Repository\n\nYou can create your own user repository by extending the SDK's `Auth0\\Laravel\\UserRepositoryAbstract` class implementing the `Auth0\\Laravel\\UserRepositoryContract` interface. Your repository class need only implement two public methods, both of which should accept a `user` array parameter.\n\n- `fromSession()` to construct a model for an authenticated user. When called, the `user` array will contain the decoded ID token for the authenticated user.\n- `fromAccessToken` to construct a model representing an access token request. When called, the `user` array will contain the decoded access token provided with the request.\n\nWhen these methods are called by the SDK, the `user` array will include all the information your application needs to construct an `Authenticatable` user model.\n\nThe default `UserRepository` implementation looks like this:\n\n```php\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Users\\{StatefulUser, StatelessUser};\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\nfinal class UserRepository extends UserRepositoryAbstract implements UserRepositoryContract\n{\n    public function fromAccessToken(array $user): ?Authenticatable\n    {\n        return new StatelessUser($user);\n    }\n\n    public function fromSession(array $user): ?Authenticatable\n    {\n        return new StatefulUser($user);\n    }\n}\n```\n\nIf you're inclined to store user information in an application database, you can expand upon this implementation to retrieve (or create) correlating user records from the database.\n\n```php\n<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Repositories;\n\nuse App\\Models\\User;\nuse Auth0\\Laravel\\{UserRepositoryAbstract, UserRepositoryContract};\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\nfinal class UserRepository extends UserRepositoryAbstract implements UserRepositoryContract\n{\n    public function fromAccessToken(array $user): ?Authenticatable\n    {\n        /*\n            $user = [ // Example of a decoded access token\n                \"iss\"   => \"https://example.auth0.com/\",\n                \"aud\"   => \"https://api.example.com/calendar/v1/\",\n                \"sub\"   => \"auth0|123456\",\n                \"exp\"   => 1458872196,\n                \"iat\"   => 1458785796,\n                \"scope\" => \"read write\",\n            ];\n        */\n\n        return User::where('auth0', $user['sub'])->first();\n    }\n\n    public function fromSession(array $user): ?Authenticatable\n    {\n        /*\n            $user = [ // Example of a decoded ID token\n                \"iss\"         => \"http://example.auth0.com\",\n                \"aud\"         => \"client_id\",\n                \"sub\"         => \"auth0|123456\",\n                \"exp\"         => 1458872196,\n                \"iat\"         => 1458785796,\n                \"name\"        => \"Jane Doe\",\n                \"email\"       => \"janedoe@example.com\",\n            ];\n        */\n\n        $user = User::updateOrCreate(\n            attributes: [\n                'auth0' => $user['sub'],\n            ],\n            values: [\n                'name' => $user['name'] ?? '',\n                'email' => $user['email'] ?? '',\n                'email_verified' => $user['email_verified'] ?? false,\n            ]\n        );\n\n        return $user;\n    }\n}\n```\n\nNote that this example returns a custom user model, `App\\Models\\User`. You can find an example of this model in the [User Models](#user-models) section below.\n\n#### Registering a Repository\n\nYou can override the SDK's default user repository by updating your application's `config/auth.php` file. Simply point the value of the `repository` key to your repository class.\n\n```php\n'providers' => [\n  'auth0-provider' => [\n    'driver' => 'auth0.provider',\n    'repository' => \\App\\Repositories\\UserRepository::class,\n  ],\n],\n```\n\n### Eloquent User Models\n\nPlease see [Eloquent.md](./Eloquent.md) for guidance on using Eloquent models with the SDK.\n"
  },
  {
    "path": "opslevel.yml",
    "content": "---\nversion: 1\nrepository:\n  owner: dx_sdks\n  tier:\n  tags:\n"
  },
  {
    "path": "phpstan.neon.dist",
    "content": "includes:\n    - ./vendor/phpstan/phpstan-strict-rules/rules.neon\n    - ./vendor/larastan/larastan/extension.neon\n\nparameters:\n    level: max\n\n    paths:\n        - src\n        - deprecated\n\n    ignoreErrors:\n        - '#Constructor of class (.*) has an unused parameter (.*).#'\n        - '#Method (.*) has parameter (.*) with no value type specified in iterable type array.#'\n        - '#no value type specified in iterable type array.#'\n        - '#Dynamic call to static method (.*).#'\n\n    reportUnmatchedIgnoredErrors: false\n    treatPhpDocTypesAsCertain: false\n    checkGenericClassInNonGenericObjectType: false\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/10.1/phpunit.xsd\">\n  <coverage>\n    <report>\n      <clover outputFile=\"coverage/clover.xml\"/>\n      <cobertura outputFile=\"coverage/cobertura.xml\"/>\n    </report>\n  </coverage>\n  <testsuites>\n    <testsuite name=\"unit\">\n      <directory>tests/Unit</directory>\n    </testsuite>\n  </testsuites>\n  <php>\n    <env name=\"CACHE_DRIVER\" value=\"array\"/>\n  </php>\n  <source>\n    <include>\n      <directory suffix=\".php\">./src/</directory>\n    </include>\n    <exclude>\n      <directory suffix=\".php\">./src/Events/</directory>\n      <directory suffix=\".php\">./src/Exceptions/</directory>\n    </exclude>\n  </source>\n</phpunit>\n"
  },
  {
    "path": "psalm.xml.dist",
    "content": "<?xml version=\"1.0\"?>\n<psalm\n    errorLevel=\"1\"\n    resolveFromConfigFile=\"true\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns=\"https://getpsalm.org/schema/config\"\n    xsi:schemaLocation=\"https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd\"\n    findUnusedBaselineEntry=\"true\"\n    findUnusedCode=\"false\"\n    allowStringToStandInForClass=\"true\"\n>\n    <projectFiles>\n        <directory name=\"src\"/>\n        <directory name=\"deprecated\"/>\n        <ignoreFiles>\n            <directory name=\"vendor\"/>\n        </ignoreFiles>\n    </projectFiles>\n\n    <issueHandlers>\n        <MixedArgument errorLevel=\"suppress\"/>\n        <MixedArgumentTypeCoercion errorLevel=\"suppress\"/>\n        <MixedAssignment errorLevel=\"suppress\"/>\n        <MixedReturnStatement errorLevel=\"suppress\"/>\n        <DeprecatedClass errorLevel=\"suppress\"/>\n        <DeprecatedInterface errorLevel=\"suppress\"/>\n        <MissingOverrideAttribute errorLevel=\"suppress\"/>\n    </issueHandlers>\n\n    <plugins>\n        <pluginClass class=\"Psalm\\LaravelPlugin\\Plugin\" />\n    </plugins>\n</psalm>\n"
  },
  {
    "path": "rector.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Rector\\Arguments\\Rector\\ClassMethod\\ArgumentAdderRector;\nuse Rector\\Arguments\\Rector\\FuncCall\\FunctionArgumentDefaultValueReplacerRector;\nuse Rector\\Arguments\\ValueObject\\{ArgumentAdder,\n    ReplaceFuncCallArgumentDefaultValue};\nuse Rector\\CodeQuality\\Rector\\Array_\\CallableThisArrayToAnonymousFunctionRector;\nuse Rector\\CodeQuality\\Rector\\Assign\\{CombinedAssignRector,\n    SplitListAssignToSeparateLineRector};\nuse Rector\\CodeQuality\\Rector\\BooleanAnd\\SimplifyEmptyArrayCheckRector;\nuse Rector\\CodeQuality\\Rector\\BooleanNot\\{ReplaceMultipleBooleanNotRector,\n    SimplifyDeMorganBinaryRector};\nuse Rector\\CodeQuality\\Rector\\Catch_\\ThrowWithPreviousExceptionRector;\nuse Rector\\CodeQuality\\Rector\\Class_\\{CompleteDynamicPropertiesRector,\n    InlineConstructorDefaultToPropertyRector};\nuse Rector\\CodeQuality\\Rector\\ClassMethod\\{InlineArrayReturnAssignRector,\n    NarrowUnionTypeDocRector,\n    OptionalParametersAfterRequiredRector,\n    ReturnTypeFromStrictScalarReturnExprRector};\nuse Rector\\CodeQuality\\Rector\\Concat\\JoinStringConcatRector;\nuse Rector\\CodeQuality\\Rector\\Empty_\\SimplifyEmptyCheckOnEmptyArrayRector;\nuse Rector\\CodeQuality\\Rector\\Equal\\UseIdenticalOverEqualWithSameTypeRector;\nuse Rector\\CodeQuality\\Rector\\Expression\\{InlineIfToExplicitIfRector,\n    TernaryFalseExpressionToIfRector};\nuse Rector\\CodeQuality\\Rector\\For_\\{ForRepeatedCountToOwnVariableRector,\n    ForToForeachRector};\nuse Rector\\CodeQuality\\Rector\\Foreach_\\{ForeachItemsAssignToEmptyArrayToAssignRector,\n    ForeachToInArrayRector,\n    SimplifyForeachToArrayFilterRector,\n    SimplifyForeachToCoalescingRector,\n    UnusedForeachValueToArrayKeysRector};\nuse Rector\\CodeQuality\\Rector\\FuncCall\\{AddPregQuoteDelimiterRector,\n    ArrayKeysAndInArrayToArrayKeyExistsRector,\n    ArrayMergeOfNonArraysToSimpleArrayRector,\n    BoolvalToTypeCastRector,\n    CallUserFuncWithArrowFunctionToInlineRector,\n    ChangeArrayPushToArrayAssignRector,\n    CompactToVariablesRector,\n    FloatvalToTypeCastRector,\n    InlineIsAInstanceOfRector,\n    IntvalToTypeCastRector,\n    IsAWithStringWithThirdArgumentRector,\n    RemoveSoleValueSprintfRector,\n    SetTypeToCastRector,\n    SimplifyFuncGetArgsCountRector,\n    SimplifyInArrayValuesRector,\n    SimplifyRegexPatternRector,\n    SimplifyStrposLowerRector,\n    SingleInArrayToCompareRector,\n    StrvalToTypeCastRector,\n    UnwrapSprintfOneArgumentRector};\nuse Rector\\CodeQuality\\Rector\\FunctionLike\\{RemoveAlwaysTrueConditionSetInConstructorRector,\n    SimplifyUselessLastVariableAssignRector,\n    SimplifyUselessVariableRector};\nuse Rector\\CodeQuality\\Rector\\Identical\\{BooleanNotIdenticalToNotIdenticalRector,\n    FlipTypeControlToUseExclusiveTypeRector,\n    GetClassToInstanceOfRector,\n    SimplifyArraySearchRector,\n    SimplifyBoolIdenticalTrueRector,\n    SimplifyConditionsRector,\n    StrlenZeroToIdenticalEmptyStringRector};\nuse Rector\\CodeQuality\\Rector\\If_\\{CombineIfRector,\n    ConsecutiveNullCompareReturnsToNullCoalesceQueueRector,\n    ExplicitBoolCompareRector,\n    ShortenElseIfRector,\n    SimplifyIfElseToTernaryRector,\n    SimplifyIfExactValueReturnValueRector,\n    SimplifyIfNotNullReturnRector,\n    SimplifyIfNullableReturnRector,\n    SimplifyIfReturnBoolRector};\nuse Rector\\CodeQuality\\Rector\\Include_\\AbsolutizeRequireAndIncludePathRector;\nuse Rector\\CodeQuality\\Rector\\Isset_\\IssetOnPropertyObjectToPropertyExistsRector;\nuse Rector\\CodeQuality\\Rector\\LogicalAnd\\{AndAssignsToSeparateLinesRector,\n    LogicalToBooleanRector};\nuse Rector\\CodeQuality\\Rector\\New_\\NewStaticToNewSelfRector;\nuse Rector\\CodeQuality\\Rector\\NotEqual\\CommonNotEqualRector;\nuse Rector\\CodeQuality\\Rector\\PropertyFetch\\ExplicitMethodCallOverMagicGetSetRector;\nuse Rector\\CodeQuality\\Rector\\Switch_\\SingularSwitchToIfRector;\nuse Rector\\CodeQuality\\Rector\\Ternary\\{ArrayKeyExistsTernaryThenValueToCoalescingRector,\n    SimplifyTautologyTernaryRector,\n    SwitchNegatedTernaryRector,\n    TernaryEmptyArrayArrayDimFetchToCoalesceRector,\n    UnnecessaryTernaryExpressionRector};\nuse Rector\\CodingStyle\\Rector\\ArrowFunction\\StaticArrowFunctionRector;\nuse Rector\\CodingStyle\\Rector\\Assign\\SplitDoubleAssignRector;\nuse Rector\\CodingStyle\\Rector\\Catch_\\CatchExceptionNameMatchingTypeRector;\nuse Rector\\CodingStyle\\Rector\\Class_\\AddArrayDefaultToArrayPropertyRector;\nuse Rector\\CodingStyle\\Rector\\ClassConst\\{RemoveFinalFromConstRector, SplitGroupedClassConstantsRector, VarConstantCommentRector};\nuse Rector\\CodingStyle\\Rector\\ClassMethod\\{FuncGetArgsToVariadicParamRector, MakeInheritedMethodVisibilitySameAsParentRector, NewlineBeforeNewAssignSetRector, RemoveDoubleUnderscoreInMethodNameRector, UnSpreadOperatorRector};\nuse Rector\\CodingStyle\\Rector\\Closure\\StaticClosureRector;\nuse Rector\\CodingStyle\\Rector\\Encapsed\\{EncapsedStringsToSprintfRector, WrapEncapsedVariableInCurlyBracesRector};\nuse Rector\\CodingStyle\\Rector\\FuncCall\\{CallUserFuncArrayToVariadicRector, CallUserFuncToMethodCallRector, ConsistentImplodeRector, ConsistentPregDelimiterRector, CountArrayToEmptyArrayComparisonRector, StrictArraySearchRector, VersionCompareFuncCallToConstantRector};\nuse Rector\\CodingStyle\\Rector\\If_\\NullableCompareToNullRector;\nuse Rector\\CodingStyle\\Rector\\Plus\\UseIncrementAssignRector;\nuse Rector\\CodingStyle\\Rector\\PostInc\\PostIncDecToPreIncDecRector;\nuse Rector\\CodingStyle\\Rector\\Property\\{AddFalseDefaultToBoolPropertyRector, SplitGroupedPropertiesRector};\nuse Rector\\CodingStyle\\Rector\\String_\\{SymplifyQuoteEscapeRector, UseClassKeywordForClassNameResolutionRector};\nuse Rector\\CodingStyle\\Rector\\Switch_\\BinarySwitchToIfElseRector;\nuse Rector\\CodingStyle\\Rector\\Ternary\\TernaryConditionVariableAssignmentRector;\nuse Rector\\CodingStyle\\Rector\\Use_\\SeparateMultiUseImportsRector;\nuse Rector\\Config\\RectorConfig;\nuse Rector\\DeadCode\\Rector\\Array_\\RemoveDuplicatedArrayKeyRector;\nuse Rector\\DeadCode\\Rector\\Assign\\{RemoveDoubleAssignRector,\n    RemoveUnusedVariableAssignRector};\nuse Rector\\DeadCode\\Rector\\BinaryOp\\RemoveDuplicatedInstanceOfRector;\nuse Rector\\DeadCode\\Rector\\BooleanAnd\\RemoveAndTrueRector;\nuse Rector\\DeadCode\\Rector\\ClassConst\\RemoveUnusedPrivateClassConstantRector;\nuse Rector\\DeadCode\\Rector\\ClassMethod\\{RemoveDelegatingParentCallRector,\n    RemoveEmptyClassMethodRector,\n    RemoveLastReturnRector,\n    RemoveUnusedConstructorParamRector,\n    RemoveUnusedPrivateMethodParameterRector,\n    RemoveUnusedPromotedPropertyRector,\n    RemoveUselessReturnTagRector};\nuse Rector\\DeadCode\\Rector\\Expression\\{RemoveDeadStmtRector,\n    SimplifyMirrorAssignRector};\nuse Rector\\DeadCode\\Rector\\For_\\{RemoveDeadContinueRector,\n    RemoveDeadIfForeachForRector,\n    RemoveDeadLoopRector};\nuse Rector\\DeadCode\\Rector\\Foreach_\\RemoveUnusedForeachKeyRector;\nuse Rector\\DeadCode\\Rector\\FunctionLike\\{RemoveDeadReturnRector,\n    RemoveDuplicatedIfReturnRector};\nuse Rector\\DeadCode\\Rector\\If_\\{\n    RemoveUnusedNonEmptyArrayBeforeForeachRector,\n    SimplifyIfElseWithSameContentRector,\n    UnwrapFutureCompatibleIfPhpVersionRector};\nuse Rector\\DeadCode\\Rector\\MethodCall\\RemoveEmptyMethodCallRector;\nuse Rector\\DeadCode\\Rector\\Node\\RemoveNonExistingVarAnnotationRector;\nuse Rector\\DeadCode\\Rector\\Plus\\RemoveDeadZeroAndOneOperationRector;\nuse Rector\\DeadCode\\Rector\\Property\\{RemoveUnusedPrivatePropertyRector,\n    RemoveUselessVarTagRector};\nuse Rector\\DeadCode\\Rector\\PropertyProperty\\RemoveNullPropertyInitializationRector;\nuse Rector\\DeadCode\\Rector\\Return_\\RemoveDeadConditionAboveReturnRector;\nuse Rector\\DeadCode\\Rector\\StaticCall\\RemoveParentCallWithoutParentRector;\nuse Rector\\DeadCode\\Rector\\Stmt\\RemoveUnreachableStatementRector;\nuse Rector\\DeadCode\\Rector\\StmtsAwareInterface\\{RemoveJustPropertyFetchForAssignRector,\n    RemoveJustVariableAssignRector};\nuse Rector\\DeadCode\\Rector\\Switch_\\RemoveDuplicatedCaseInSwitchRector;\nuse Rector\\DeadCode\\Rector\\Ternary\\TernaryToBooleanOrFalseToBooleanAndRector;\nuse Rector\\DeadCode\\Rector\\TryCatch\\RemoveDeadTryCatchRector;\nuse Rector\\DependencyInjection\\Rector\\Class_\\ActionInjectionToConstructorInjectionRector;\nuse Rector\\EarlyReturn\\Rector\\Foreach_\\ChangeNestedForeachIfsToEarlyContinueRector;\nuse Rector\\EarlyReturn\\Rector\\If_\\{\n    ChangeIfElseValueAssignToEarlyReturnRector,\n    ChangeNestedIfsToEarlyReturnRector,\n    ChangeOrIfContinueToMultiContinueRector,\n    RemoveAlwaysElseRector};\nuse Rector\\EarlyReturn\\Rector\\Return_\\{\n    ReturnBinaryAndToEarlyReturnRector,\n    ReturnBinaryOrToEarlyReturnRector};\nuse Rector\\EarlyReturn\\Rector\\StmtsAwareInterface\\ReturnEarlyIfVariableRector;\n\nuse Rector\\Naming\\Rector\\Foreach_\\{RenameForeachValueVariableToMatchExprVariableRector,\n    RenameForeachValueVariableToMatchMethodCallReturnTypeRector};\nuse Rector\\Php52\\Rector\\Property\\VarToPublicPropertyRector;\nuse Rector\\Php71\\Rector\\FuncCall\\RemoveExtraParametersRector;\nuse Rector\\Php80\\Rector\\Catch_\\RemoveUnusedVariableInCatchRector;\nuse Rector\\Php80\\Rector\\Class_\\{ClassPropertyAssignToConstructorPromotionRector,\n    StringableForToStringRector};\nuse Rector\\Php80\\Rector\\ClassConstFetch\\ClassOnThisVariableObjectRector;\nuse Rector\\Php80\\Rector\\ClassMethod\\{AddParamBasedOnParentClassMethodRector,\n    FinalPrivateToPrivateVisibilityRector,\n    SetStateToStaticRector};\nuse Rector\\Php80\\Rector\\FuncCall\\{ClassOnObjectRector,\n    Php8ResourceReturnToObjectRector,\n    TokenGetAllToObjectRector};\n\nuse Rector\\Php80\\Rector\\Identical\\{StrEndsWithRector,\n    StrStartsWithRector};\nuse Rector\\Php80\\Rector\\NotIdentical\\StrContainsRector;\nuse Rector\\Php80\\Rector\\Switch_\\ChangeSwitchToMatchRector;\nuse Rector\\Php80\\Rector\\Ternary\\GetDebugTypeRector;\nuse Rector\\PHPUnit\\Rector\\ClassMethod\\RemoveEmptyTestMethodRector;\nuse Rector\\Privatization\\Rector\\Class_\\{ChangeGlobalVariablesToPropertiesRector,\n    ChangeReadOnlyVariableWithDefaultValueToConstantRector,\n    FinalizeClassesWithoutChildrenRector};\nuse Rector\\Privatization\\Rector\\ClassMethod\\PrivatizeFinalClassMethodRector;\nuse Rector\\Privatization\\Rector\\Property\\{ChangeReadOnlyPropertyWithDefaultValueToConstantRector,\n    PrivatizeFinalClassPropertyRector};\nuse Rector\\PSR4\\Rector\\FileWithoutNamespace\\NormalizeNamespaceByPSR4ComposerAutoloadRector;\nuse Rector\\PSR4\\Rector\\Namespace_\\MultipleClassFileToPsr4ClassesRector;\nuse Rector\\Renaming\\Rector\\FuncCall\\RenameFunctionRector;\nuse Rector\\Transform\\Rector\\FuncCall\\FuncCallToConstFetchRector;\nuse Rector\\Transform\\Rector\\StaticCall\\StaticCallToFuncCallRector;\nuse Rector\\Transform\\ValueObject\\StaticCallToFuncCall;\nuse Rector\\TypeDeclaration\\Rector\\ArrowFunction\\AddArrowFunctionReturnTypeRector;\nuse Rector\\TypeDeclaration\\Rector\\Class_\\{PropertyTypeFromStrictSetterGetterRector,\n    ReturnTypeFromStrictTernaryRector};\nuse Rector\\TypeDeclaration\\Rector\\ClassMethod\\{AddMethodCallBasedStrictParamTypeRector,\n    AddParamTypeBasedOnPHPUnitDataProviderRector,\n    AddReturnTypeDeclarationBasedOnParentClassMethodRector,\n    AddVoidReturnTypeWhereNoReturnRector,\n    ArrayShapeFromConstantArrayReturnRector,\n    ParamAnnotationIncorrectNullableRector,\n    ParamTypeByMethodCallTypeRector,\n    ParamTypeByParentCallTypeRector,\n    ReturnAnnotationIncorrectNullableRector,\n    ReturnNeverTypeRector,\n    ReturnTypeFromReturnDirectArrayRector,\n    ReturnTypeFromReturnNewRector,\n    ReturnTypeFromStrictBoolReturnExprRector,\n    ReturnTypeFromStrictConstantReturnRector,\n    ReturnTypeFromStrictNativeCallRector,\n    ReturnTypeFromStrictNewArrayRector,\n    ReturnTypeFromStrictTypedCallRector,\n    ReturnTypeFromStrictTypedPropertyRector};\nuse Rector\\TypeDeclaration\\Rector\\Closure\\AddClosureReturnTypeRector;\nuse Rector\\TypeDeclaration\\Rector\\Empty_\\EmptyOnNullableObjectToInstanceOfRector;\nuse Rector\\TypeDeclaration\\Rector\\FunctionLike\\{AddParamTypeSplFixedArrayRector,\n    AddReturnTypeDeclarationFromYieldsRector};\nuse Rector\\TypeDeclaration\\Rector\\Param\\ParamTypeFromStrictTypedPropertyRector;\nuse Rector\\TypeDeclaration\\Rector\\Property\\{TypedPropertyFromAssignsRector,\n    TypedPropertyFromStrictConstructorRector,\n    TypedPropertyFromStrictGetterMethodReturnTypeRector,\n    TypedPropertyFromStrictSetUpRector,\n    VarAnnotationIncorrectNullableRector};\n\nreturn static function (RectorConfig $rectorConfig): void {\n    $rectorConfig->paths([\n        __DIR__ . '/config',\n        __DIR__ . '/src',\n    ]);\n\n    // Laravel AuthManager::extend() resolves callbacks in a way that breaks with\n    // static arrow functions on Laravel 13; keep non-static `fn` here.\n    $rectorConfig->skip([\n        StaticArrowFunctionRector::class => [\n            __DIR__ . '/src/ServiceProviderAbstract.php',\n        ],\n        StaticClosureRector::class => [\n            __DIR__ . '/src/ServiceProviderAbstract.php',\n        ],\n    ]);\n\n    $rectorConfig->ruleWithConfiguration(\n        RenameFunctionRector::class,\n        [\n            'chop' => 'rtrim',\n            'doubleval' => 'floatval',\n            'fputs' => 'fwrite',\n            'gzputs' => 'gzwrites',\n            'ini_alter' => 'ini_set',\n            'is_double' => 'is_float',\n            'is_integer' => 'is_int',\n            'is_long' => 'is_int',\n            'is_real' => 'is_float',\n            'is_writeable' => 'is_writable',\n            'join' => 'implode',\n            'key_exists' => 'array_key_exists',\n            'mbstrcut' => 'mb_strcut',\n            'mbstrlen' => 'mb_strlen',\n            'mbstrpos' => 'mb_strpos',\n            'mbstrrpos' => 'mb_strrpos',\n            'mbsubstr' => 'mb_substr',\n            'pos' => 'current',\n            'sizeof' => 'count',\n            'split' => 'explode',\n            'strchr' => 'strstr',\n        ],\n    );\n\n    $rectorConfig->ruleWithConfiguration(\n        StaticCallToFuncCallRector::class,\n        [\n            new StaticCallToFuncCall('Nette\\\\Utils\\\\Strings', 'contains', 'str_contains'),\n            new StaticCallToFuncCall('Nette\\\\Utils\\\\Strings', 'endsWith', 'str_ends_with'),\n            new StaticCallToFuncCall('Nette\\\\Utils\\\\Strings', 'startsWith', 'str_starts_with'),\n        ],\n    );\n\n    $rectorConfig->ruleWithConfiguration(\n        ArgumentAdderRector::class,\n        [new ArgumentAdder('Nette\\\\Utils\\\\Strings', 'replace', 2, 'replacement', '')],\n    );\n\n    $rectorConfig->ruleWithConfiguration(\n        RenameFunctionRector::class,\n        [\n            'pg_clientencoding' => 'pg_client_encoding',\n            'pg_cmdtuples' => 'pg_affected_rows',\n            'pg_errormessage' => 'pg_last_error',\n            'pg_fieldisnull' => 'pg_field_is_null',\n            'pg_fieldname' => 'pg_field_name',\n            'pg_fieldnum' => 'pg_field_num',\n            'pg_fieldprtlen' => 'pg_field_prtlen',\n            'pg_fieldsize' => 'pg_field_size',\n            'pg_fieldtype' => 'pg_field_type',\n            'pg_freeresult' => 'pg_free_result',\n            'pg_getlastoid' => 'pg_last_oid',\n            'pg_loclose' => 'pg_lo_close',\n            'pg_locreate' => 'pg_lo_create',\n            'pg_loexport' => 'pg_lo_export',\n            'pg_loimport' => 'pg_lo_import',\n            'pg_loopen' => 'pg_lo_open',\n            'pg_loread' => 'pg_lo_read',\n            'pg_loreadall' => 'pg_lo_read_all',\n            'pg_lounlink' => 'pg_lo_unlink',\n            'pg_lowrite' => 'pg_lo_write',\n            'pg_numfields' => 'pg_num_fields',\n            'pg_numrows' => 'pg_num_rows',\n            'pg_result' => 'pg_fetch_result',\n            'pg_setclientencoding' => 'pg_set_client_encoding',\n        ],\n    );\n\n    $rectorConfig->ruleWithConfiguration(\n        FunctionArgumentDefaultValueReplacerRector::class,\n        [\n            new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge'),\n            new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'),\n            new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '', '!='),\n            new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '!', '!='),\n            new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'g', 'gt'),\n            new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'l', 'lt'),\n            new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge'),\n            new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'),\n            new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'n', 'ne'),\n        ],\n    );\n\n    $rectorConfig->ruleWithConfiguration(\n        FuncCallToConstFetchRector::class,\n        [\n            'php_sapi_name' => 'PHP_SAPI',\n            'pi' => 'M_PI',\n        ],\n    );\n\n    $rectorConfig->rules([\n        AbsolutizeRequireAndIncludePathRector::class,\n        // ActionInjectionToConstructorInjectionRector::class,\n        // AddArrayDefaultToArrayPropertyRector::class,\n        AddArrowFunctionReturnTypeRector::class,\n        // AddClosureReturnTypeRector::class,\n        // AddFalseDefaultToBoolPropertyRector::class,\n        AddMethodCallBasedStrictParamTypeRector::class,\n        AddParamBasedOnParentClassMethodRector::class,\n        AddParamTypeBasedOnPHPUnitDataProviderRector::class,\n        AddParamTypeSplFixedArrayRector::class,\n        // AddPregQuoteDelimiterRector::class,\n        AddReturnTypeDeclarationBasedOnParentClassMethodRector::class,\n        AddReturnTypeDeclarationFromYieldsRector::class,\n        AddVoidReturnTypeWhereNoReturnRector::class,\n        AndAssignsToSeparateLinesRector::class,\n        ArrayKeyExistsTernaryThenValueToCoalescingRector::class,\n        // ArrayKeysAndInArrayToArrayKeyExistsRector::class,\n        ArrayMergeOfNonArraysToSimpleArrayRector::class,\n        // ArrayShapeFromConstantArrayReturnRector::class,\n        // BinarySwitchToIfElseRector::class,\n        BooleanNotIdenticalToNotIdenticalRector::class,\n        //BoolvalToTypeCastRector::class,\n        //CallableThisArrayToAnonymousFunctionRector::class,\n        CallUserFuncArrayToVariadicRector::class,\n        CallUserFuncToMethodCallRector::class,\n        CallUserFuncWithArrowFunctionToInlineRector::class,\n        CatchExceptionNameMatchingTypeRector::class,\n        ChangeArrayPushToArrayAssignRector::class,\n        // ChangeGlobalVariablesToPropertiesRector::class,\n        ChangeIfElseValueAssignToEarlyReturnRector::class,\n        ChangeNestedForeachIfsToEarlyContinueRector::class,\n        ChangeNestedIfsToEarlyReturnRector::class,\n        ChangeOrIfContinueToMultiContinueRector::class,\n        // ChangeReadOnlyPropertyWithDefaultValueToConstantRector::class,\n        // ChangeReadOnlyVariableWithDefaultValueToConstantRector::class,\n        ChangeSwitchToMatchRector::class,\n        ClassOnObjectRector::class,\n        ClassOnThisVariableObjectRector::class,\n        ClassPropertyAssignToConstructorPromotionRector::class,\n        CombinedAssignRector::class,\n        CombineIfRector::class,\n        CommonNotEqualRector::class,\n        CompactToVariablesRector::class,\n        CompleteDynamicPropertiesRector::class,\n        ConsecutiveNullCompareReturnsToNullCoalesceQueueRector::class,\n        ConsistentImplodeRector::class,\n        // ConsistentPregDelimiterRector::class,\n        CountArrayToEmptyArrayComparisonRector::class,\n        EmptyOnNullableObjectToInstanceOfRector::class,\n        EncapsedStringsToSprintfRector::class,\n        ExplicitBoolCompareRector::class,\n        // ExplicitMethodCallOverMagicGetSetRector::class,\n        // FinalizeClassesWithoutChildrenRector::class,\n        FinalPrivateToPrivateVisibilityRector::class,\n        FlipTypeControlToUseExclusiveTypeRector::class,\n        //FloatvalToTypeCastRector::class,\n        ForeachItemsAssignToEmptyArrayToAssignRector::class,\n        ForeachToInArrayRector::class,\n        ForRepeatedCountToOwnVariableRector::class,\n        // ForToForeachRector::class,\n        FuncGetArgsToVariadicParamRector::class,\n        //GetClassToInstanceOfRector::class,\n        GetDebugTypeRector::class,\n        InlineArrayReturnAssignRector::class,\n        InlineConstructorDefaultToPropertyRector::class,\n        InlineIfToExplicitIfRector::class,\n        InlineIsAInstanceOfRector::class,\n        //IntvalToTypeCastRector::class,\n        IsAWithStringWithThirdArgumentRector::class,\n        IssetOnPropertyObjectToPropertyExistsRector::class,\n        JoinStringConcatRector::class,\n        LogicalToBooleanRector::class,\n        MakeInheritedMethodVisibilitySameAsParentRector::class,\n        // MultipleClassFileToPsr4ClassesRector::class,\n        // NarrowUnionTypeDocRector::class,\n        NewlineBeforeNewAssignSetRector::class,\n        NewStaticToNewSelfRector::class,\n        // NormalizeNamespaceByPSR4ComposerAutoloadRector::class,\n        NullableCompareToNullRector::class,\n        OptionalParametersAfterRequiredRector::class,\n        // ParamAnnotationIncorrectNullableRector::class,\n        ParamTypeByMethodCallTypeRector::class,\n        ParamTypeByParentCallTypeRector::class,\n        // ParamTypeFromStrictTypedPropertyRector::class,\n        // Php8ResourceReturnToObjectRector::class,\n        PostIncDecToPreIncDecRector::class,\n        PrivatizeFinalClassMethodRector::class,\n        PrivatizeFinalClassPropertyRector::class,\n        PropertyTypeFromStrictSetterGetterRector::class,\n        RemoveAlwaysElseRector::class,\n        // RemoveAlwaysTrueConditionSetInConstructorRector::class,\n        RemoveAndTrueRector::class,\n        RemoveDeadConditionAboveReturnRector::class,\n        RemoveDeadContinueRector::class,\n        RemoveDeadIfForeachForRector::class,\n        RemoveDeadLoopRector::class,\n        RemoveDeadReturnRector::class,\n        RemoveDeadStmtRector::class,\n        RemoveDeadTryCatchRector::class,\n        RemoveDeadZeroAndOneOperationRector::class,\n        // RemoveDelegatingParentCallRector::class,\n        RemoveDoubleAssignRector::class,\n        // RemoveDoubleUnderscoreInMethodNameRector::class,\n        RemoveDuplicatedArrayKeyRector::class,\n        RemoveDuplicatedCaseInSwitchRector::class,\n        // RemoveDuplicatedIfReturnRector::class,\n        // RemoveDuplicatedInstanceOfRector::class,\n        RemoveEmptyClassMethodRector::class,\n        // RemoveEmptyMethodCallRector::class,\n        // RemoveEmptyTestMethodRector::class,\n        RemoveExtraParametersRector::class,\n        RemoveFinalFromConstRector::class,\n        // RemoveJustPropertyFetchForAssignRector::class,\n        // RemoveJustVariableAssignRector::class,\n        // RemoveLastReturnRector::class,\n        // RemoveNonExistingVarAnnotationRector::class,\n        RemoveNullPropertyInitializationRector::class,\n        RemoveParentCallWithoutParentRector::class,\n        RemoveSoleValueSprintfRector::class,\n        RemoveUnreachableStatementRector::class,\n        RemoveUnusedConstructorParamRector::class,\n        RemoveUnusedForeachKeyRector::class,\n        RemoveUnusedNonEmptyArrayBeforeForeachRector::class,\n        RemoveUnusedPrivateClassConstantRector::class,\n        RemoveUnusedPrivateMethodParameterRector::class,\n        RemoveUnusedPrivatePropertyRector::class,\n        RemoveUnusedPromotedPropertyRector::class,\n        RemoveUnusedVariableAssignRector::class,\n        RemoveUnusedVariableInCatchRector::class,\n        RemoveUselessReturnTagRector::class,\n        RemoveUselessVarTagRector::class,\n        RenameForeachValueVariableToMatchExprVariableRector::class,\n        RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class,\n        ReplaceMultipleBooleanNotRector::class,\n        // ReturnAnnotationIncorrectNullableRector::class,\n        // ReturnBinaryAndToEarlyReturnRector::class,\n        ReturnBinaryOrToEarlyReturnRector::class,\n        ReturnEarlyIfVariableRector::class,\n        ReturnNeverTypeRector::class,\n        ReturnTypeFromReturnDirectArrayRector::class,\n        ReturnTypeFromReturnNewRector::class,\n        //ReturnTypeFromStrictBoolReturnExprRector::class,\n        ReturnTypeFromStrictConstantReturnRector::class,\n        ReturnTypeFromStrictNativeCallRector::class,\n        ReturnTypeFromStrictNewArrayRector::class,\n        // ReturnTypeFromStrictScalarReturnExprRector::class,\n        ReturnTypeFromStrictTernaryRector::class,\n        ReturnTypeFromStrictTypedCallRector::class,\n        ReturnTypeFromStrictTypedPropertyRector::class,\n        SeparateMultiUseImportsRector::class,\n        SetStateToStaticRector::class,\n        SetTypeToCastRector::class,\n        ShortenElseIfRector::class,\n        SimplifyArraySearchRector::class,\n        SimplifyBoolIdenticalTrueRector::class,\n        SimplifyConditionsRector::class,\n        SimplifyDeMorganBinaryRector::class,\n        SimplifyEmptyArrayCheckRector::class,\n        SimplifyEmptyCheckOnEmptyArrayRector::class,\n        // SimplifyForeachToArrayFilterRector::class,\n        SimplifyForeachToCoalescingRector::class,\n        SimplifyFuncGetArgsCountRector::class,\n        SimplifyIfElseToTernaryRector::class,\n        SimplifyIfElseWithSameContentRector::class,\n        // SimplifyIfExactValueReturnValueRector::class,\n        SimplifyIfNotNullReturnRector::class,\n        SimplifyIfNullableReturnRector::class,\n        SimplifyIfReturnBoolRector::class,\n        SimplifyInArrayValuesRector::class,\n        SimplifyMirrorAssignRector::class,\n        SimplifyRegexPatternRector::class,\n        SimplifyStrposLowerRector::class,\n        SimplifyTautologyTernaryRector::class,\n        // SimplifyUselessLastVariableAssignRector::class,\n        SimplifyUselessVariableRector::class,\n        SingleInArrayToCompareRector::class,\n        SingularSwitchToIfRector::class,\n        SplitDoubleAssignRector::class,\n        SplitGroupedClassConstantsRector::class,\n        SplitGroupedPropertiesRector::class,\n        // SplitListAssignToSeparateLineRector::class,\n        StaticArrowFunctionRector::class,\n        StaticClosureRector::class,\n        StrContainsRector::class,\n        StrEndsWithRector::class,\n        StrictArraySearchRector::class,\n        StringableForToStringRector::class,\n        StrlenZeroToIdenticalEmptyStringRector::class,\n        StrStartsWithRector::class,\n        //StrvalToTypeCastRector::class,\n        SwitchNegatedTernaryRector::class,\n        SymplifyQuoteEscapeRector::class,\n        TernaryConditionVariableAssignmentRector::class,\n        TernaryEmptyArrayArrayDimFetchToCoalesceRector::class,\n        TernaryFalseExpressionToIfRector::class,\n        TernaryToBooleanOrFalseToBooleanAndRector::class,\n        ThrowWithPreviousExceptionRector::class,\n        // TokenGetAllToObjectRector::class,\n        TypedPropertyFromAssignsRector::class,\n        TypedPropertyFromStrictConstructorRector::class,\n        // TypedPropertyFromStrictGetterMethodReturnTypeRector::class,\n        TypedPropertyFromStrictSetUpRector::class,\n        UnnecessaryTernaryExpressionRector::class,\n        // UnSpreadOperatorRector::class,\n        UnusedForeachValueToArrayKeysRector::class,\n        UnwrapFutureCompatibleIfPhpVersionRector::class,\n        UnwrapSprintfOneArgumentRector::class,\n        UseClassKeywordForClassNameResolutionRector::class,\n        UseIdenticalOverEqualWithSameTypeRector::class,\n        //UseIncrementAssignRector::class,\n        // VarAnnotationIncorrectNullableRector::class,\n        // VarConstantCommentRector::class,\n        VarToPublicPropertyRector::class,\n        VersionCompareFuncCallToConstantRector::class,\n        WrapEncapsedVariableInCurlyBracesRector::class,\n    ]);\n};\n"
  },
  {
    "path": "src/Auth/Guard.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Auth;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\Laravel\\Guards\\{AuthenticationGuard, AuthenticationGuardContract, AuthorizationGuard, AuthorizationGuardContract, GuardAbstract, GuardContract};\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\n/**\n * @deprecated 7.8.0 Please migrate to using either Auth0\\Laravel\\Guards\\AuthenticationGuard or Auth0\\Laravel\\Guards\\AuthorizationGuard.\n *\n * @api\n */\nfinal class Guard extends GuardAbstract implements GuardContract\n{\n    private ?AuthenticationGuardContract $authenticator = null;\n\n    private ?AuthorizationGuardContract $authorizer = null;\n\n    private ?int $credentialSource = null;\n\n    /**\n     * @param null|int $source Credential source in which to search. Defaults to searching all sources.\n     */\n    public function find(\n        ?int $source = null,\n    ): ?CredentialEntityContract {\n        $token = null;\n        $session = null;\n\n        if ($this->isImpersonating()) {\n            return $this->getImposter();\n        }\n\n        if (null === $source || self::SOURCE_TOKEN === $source) {\n            $token = $this->getAuthorizationGuard()->findToken();\n        }\n\n        if (null === $source && ! $token instanceof CredentialEntityContract || self::SOURCE_SESSION === $source) {\n            $session = $this->getAuthenticationGuard()->findSession();\n        }\n\n        return $token ?? $session ?? null;\n    }\n\n    public function forgetUser(): self\n    {\n        $this->setCredential();\n\n        return $this;\n    }\n\n    public function getCredential(): ?CredentialEntityContract\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter();\n        }\n\n        $token = null;\n        $session = null;\n        $source = $this->getCredentialSource();\n\n        if (null === $source || self::SOURCE_TOKEN === $source) {\n            $token = $this->getAuthorizationGuard()->getCredential();\n        }\n\n        if (null === $source && ! $token instanceof CredentialEntityContract || self::SOURCE_SESSION === $source) {\n            $session = $this->getAuthenticationGuard()->getCredential();\n        }\n\n        return $token ?? $session ?? null;\n    }\n\n    /**\n     * Sets the currently authenticated user for the guard.\n     *\n     * @param null|CredentialEntityContract $credential Optional. The credential to use.\n     * @param null|int                      $source     Optional. The source context in which to assign the user. Defaults to all sources.\n     */\n    public function login(\n        ?CredentialEntityContract $credential,\n        ?int $source = null,\n    ): GuardContract {\n        $this->stopImpersonating();\n\n        if (null === $source || self::SOURCE_TOKEN === $source) {\n            $this->getAuthorizationGuard()->login($credential);\n        }\n\n        if (null === $source || self::SOURCE_SESSION === $source) {\n            $this->getAuthenticationGuard()->login($credential);\n        }\n\n        $this->credentialSource = $source;\n\n        return $this;\n    }\n\n    public function logout(): GuardContract\n    {\n        if ($this->isImpersonating()) {\n            $this->stopImpersonating();\n\n            return $this;\n        }\n\n        $source = $this->getCredentialSource();\n\n        if (null === $source || self::SOURCE_TOKEN === $source) {\n            $this->getAuthorizationGuard()->logout();\n        }\n\n        if (null === $source || self::SOURCE_SESSION === $source) {\n            $this->getAuthenticationGuard()->logout();\n        }\n\n        return $this;\n    }\n\n    public function refreshUser(): void\n    {\n        if ($this->isImpersonating()) {\n            return;\n        }\n\n        $source = $this->getCredentialSource();\n\n        if (null === $source || self::SOURCE_TOKEN === $source) {\n            $this->getAuthorizationGuard()->refreshUser();\n        }\n\n        if (null === $source || self::SOURCE_SESSION === $source) {\n            $this->getAuthenticationGuard()->refreshUser();\n        }\n    }\n\n    public function setCredential(\n        ?CredentialEntityContract $credential = null,\n        ?int $source = null,\n    ): GuardContract {\n        $this->stopImpersonating();\n\n        if (null === $source || self::SOURCE_TOKEN === $source) {\n            $this->getAuthorizationGuard()->setCredential($credential);\n        }\n\n        if (null === $source || self::SOURCE_SESSION === $source) {\n            $this->getAuthenticationGuard()->setCredential($credential);\n        }\n\n        $this->credentialSource = $source;\n\n        return $this;\n    }\n\n    /**\n     * @param CredentialEntityContract $credential\n     * @param ?int                     $source\n     */\n    public function setImpersonating(\n        CredentialEntityContract $credential,\n        ?int $source = null,\n    ): self {\n        $this->impersonationSource = $source;\n        $this->impersonating = $credential;\n\n        return $this;\n    }\n\n    public function setUser(\n        Authenticatable $user,\n    ): self {\n        if ($this->isImpersonating()) {\n            if ($this->getImposter()?->getUser() === $user) {\n                return $this;\n            }\n\n            $this->stopImpersonating();\n        }\n\n        $source = $this->getCredentialSource();\n\n        if (null === $source || self::SOURCE_TOKEN === $source) {\n            $this->getAuthorizationGuard()->setUser($user);\n        }\n\n        if (null === $source || self::SOURCE_SESSION === $source) {\n            $this->getAuthenticationGuard()->setUser($user);\n        }\n\n        return $this;\n    }\n\n    public function user(): ?Authenticatable\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter()?->getUser();\n        }\n\n        $credential = $this->getCredential();\n\n        if ($credential instanceof CredentialEntityContract) {\n            return $credential->getUser();\n        }\n\n        // $source = $this->getCredentialSource();\n        // $token = null;\n        // $session = null;\n\n        // if (null === $source || self::SOURCE_TOKEN === $source) {\n        //     $token = $this->getAuthorizationGuard()->user();\n        // }\n\n        // if (null === $source || self::SOURCE_SESSION === $source) {\n        //     $session = $this->getAuthenticationGuard()->user();\n        // }\n\n        // return $token ?? $session ?? null;\n\n        return null;\n    }\n\n    private function getAuthenticationGuard(): AuthenticationGuardContract\n    {\n        $this->sdk();\n\n        return $this->authenticator ??= new AuthenticationGuard(name: $this->name, config: $this->config, sdk: $this->sdk);\n    }\n\n    private function getAuthorizationGuard(): AuthorizationGuardContract\n    {\n        $this->sdk();\n\n        return $this->authorizer ??= new AuthorizationGuard(name: $this->name, config: $this->config, sdk: $this->sdk);\n    }\n\n    private function getCredentialSource(): ?int\n    {\n        return $this->credentialSource;\n    }\n}\n"
  },
  {
    "path": "src/Auth0.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Entities\\InstanceEntityTrait;\n\n/**\n * Auth0 Laravel SDK service provider. Provides access to the SDK's methods.\n *\n * @method static \\Auth0\\SDK\\Configuration\\SdkConfiguration   getConfiguration()\n * @method static null|object                                 getCredentials()\n * @method static null|string                                 getGuardConfigurationKey()\n * @method static \\Auth0\\SDK\\Contract\\Auth0Interface          getSdk()\n * @method static \\Auth0\\SDK\\Contract\\API\\ManagementInterface management()\n * @method static self                                        setGuardConfigurationKey(null|string $guardConfigurationKey = null)\n * @method static \\Auth0\\SDK\\Contract\\Auth0Interface          setSdk(\\Auth0\\SDK\\Contract\\Auth0Interface $sdk)\n * @method static self                                        reset()\n * @method static self                                        setConfiguration(\\Auth0\\SDK\\Configuration\\SdkConfiguration|array|null $configuration = null)\n *\n * @see Service\n * @see ServiceAbstract\n * @see Entities\\InstanceEntityAbstract\n * @see InstanceEntityTrait\n *\n * @codeCoverageIgnore\n *\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Service instead.\n *\n * @api\n */\nfinal class Auth0 extends ServiceAbstract implements ServiceContract\n{\n    use InstanceEntityTrait;\n}\n"
  },
  {
    "path": "src/Bridges/BridgeAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class BridgeAbstract\n{\n}\n"
  },
  {
    "path": "src/Bridges/BridgeContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\n/**\n * @api\n */\ninterface BridgeContract\n{\n}\n"
  },
  {
    "path": "src/Bridges/CacheBridge.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\n/**\n * Bridges the Laravel's Cache API with the PSR-6's CacheItemPoolInterface interface.\n *\n * @internal\n *\n * @api\n */\nfinal class CacheBridge extends CacheBridgeAbstract implements CacheBridgeContract\n{\n}\n"
  },
  {
    "path": "src/Bridges/CacheBridgeAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\nuse DateTimeInterface;\nuse Illuminate\\Cache\\CacheManager;\nuse Illuminate\\Contracts\\Cache\\Store;\nuse Psr\\Cache\\CacheItemInterface;\n\nuse RuntimeException;\n\nuse function is_string;\n\n/**\n * @api\n */\nabstract class CacheBridgeAbstract extends BridgeAbstract\n{\n    /**\n     * @var array<array{item: CacheItemInterface, expiration: null|DateTimeInterface|int}>\n     */\n    protected array $deferred = [];\n\n    final public function clear(): bool\n    {\n        $this->deferred = [];\n\n        return $this->getCache()->flush();\n    }\n\n    final public function commit(): bool\n    {\n        $success = true;\n\n        foreach (array_keys($this->deferred) as $singleDeferred) {\n            $item = $this->getDeferred((string) $singleDeferred);\n\n            // @codeCoverageIgnoreStart\n            if ($item instanceof CacheItemInterface && ! $this->save($item)) {\n                $success = false;\n            }\n            // @codeCoverageIgnoreEnd\n        }\n\n        $this->deferred = [];\n\n        return $success;\n    }\n\n    /**\n     * @param string $key the key for which to return the corresponding Cache Item\n     */\n    final public function deleteItem(string $key): bool\n    {\n        return $this->getCache()->forget($key);\n    }\n\n    final public function deleteItems(array $keys): bool\n    {\n        $deleted = true;\n\n        foreach ($keys as $key) {\n            if (! $this->deleteItem($key)) {\n                $deleted = false;\n            }\n        }\n\n        return $deleted;\n    }\n\n    final public function getItem(string $key): CacheItemInterface\n    {\n        $value = $this->getCache()->get($key);\n\n        if (false === $value) {\n            return CacheItemBridge::miss($key);\n        }\n\n        return $this->createItem($key, $value);\n    }\n\n    /**\n     * @param string[] $keys\n     *\n     * @return CacheItemInterface[]\n     */\n    final public function getItems(array $keys = []): iterable\n    {\n        if ([] === $keys) {\n            return [];\n        }\n\n        $results = $this->getCache()->many($keys);\n        $items = [];\n\n        foreach ($results as $key => $value) {\n            $key = (string) $key;\n            $items[$key] = $this->createItem($key, $value);\n        }\n\n        return $items;\n    }\n\n    /**\n     * @param string $key the key for which to return the corresponding Cache Item\n     */\n    final public function hasItem(string $key): bool\n    {\n        return $this->getItem($key)\n            ->isHit();\n    }\n\n    final public function save(CacheItemInterface $item): bool\n    {\n        if (! $item instanceof CacheItemBridge) {\n            return false;\n        }\n\n        $value = serialize($item->getRawValue());\n        $key = $item->getKey();\n        $expires = $item->getExpiration();\n\n        if ($expires->getTimestamp() <= time()) {\n            return $this->deleteItem($key);\n        }\n\n        $ttl = $expires->getTimestamp() - time();\n\n        return $this->getCache()->put($key, $value, $ttl);\n    }\n\n    final public function saveDeferred(CacheItemInterface $item): bool\n    {\n        if (! $item instanceof CacheItemBridge) {\n            return false;\n        }\n\n        $this->deferred[$item->getKey()] = [\n            'item' => $item,\n            'expiration' => $item->getExpiration(),\n        ];\n\n        return true;\n    }\n\n    protected function createItem(string $key, mixed $value): CacheItemInterface\n    {\n        if (! is_string($value)) {\n            return CacheItemBridge::miss($key);\n        }\n\n        $value = unserialize($value);\n\n        if (false === $value) {\n            return CacheItemBridge::miss($key);\n        }\n\n        return new CacheItemBridge($key, $value, true);\n    }\n\n    protected function getCache(): Store\n    {\n        $cache = cache();\n\n        // @codeCoverageIgnoreStart\n        if (! $cache instanceof CacheManager) {\n            throw new RuntimeException('Cache store is not an instance of Illuminate\\Contracts\\Cache\\CacheManager');\n        }\n        // @codeCoverageIgnoreEnd\n\n        return $cache->getStore();\n    }\n\n    /**\n     * @param string $key the key for which to return the corresponding Cache Item\n     *\n     * @codeCoverageIgnore\n     */\n    protected function getDeferred(string $key): ?CacheItemInterface\n    {\n        if (! isset($this->deferred[$key])) {\n            return null;\n        }\n\n        $deferred = $this->deferred[$key];\n        $item = clone $deferred['item'];\n        $expires = $deferred['expiration'];\n\n        if ($expires instanceof DateTimeInterface) {\n            $expires = $expires->getTimestamp();\n        }\n\n        if (null !== $expires && $expires <= time()) {\n            unset($this->deferred[$key]);\n\n            return null;\n        }\n\n        return $item;\n    }\n}\n"
  },
  {
    "path": "src/Bridges/CacheBridgeContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\nuse Psr\\Cache\\CacheItemPoolInterface;\n\n/**\n * @api\n */\ninterface CacheBridgeContract extends BridgeContract, CacheItemPoolInterface\n{\n}\n"
  },
  {
    "path": "src/Bridges/CacheItemBridge.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\nuse DateInterval;\nuse DateTimeImmutable;\nuse DateTimeInterface;\n\nuse function is_int;\n\n/**\n * Bridges the Laravel's Cache API with the PSR-6's CacheItemInterface interface.\n *\n * @internal\n *\n * @api\n */\nfinal class CacheItemBridge extends CacheItemBridgeAbstract implements CacheItemBridgeContract\n{\n    public function expiresAfter(int | DateInterval | null $time): static\n    {\n        $this->expiration = match (true) {\n            null === $time => new DateTimeImmutable('now +1 year'),\n            is_int($time) => new DateTimeImmutable('now +' . (string) $time . ' seconds'),\n            $time instanceof DateInterval => (new DateTimeImmutable())->add($time),\n        };\n\n        return $this;\n    }\n\n    public function expiresAt(?DateTimeInterface $expiration): static\n    {\n        $this->expiration = $expiration ?? new DateTimeImmutable('now +1 year');\n\n        return $this;\n    }\n\n    public function set(mixed $value): static\n    {\n        $this->value = $value;\n\n        return $this;\n    }\n\n    public static function miss(string $key): self\n    {\n        return new self(\n            key: $key,\n            value: null,\n            hit: false,\n        );\n    }\n}\n"
  },
  {
    "path": "src/Bridges/CacheItemBridgeAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\nuse DateInterval;\nuse DateTimeImmutable;\nuse DateTimeInterface;\n\n/**\n * @api\n */\nabstract class CacheItemBridgeAbstract extends BridgeAbstract\n{\n    public function __construct(\n        protected string $key,\n        protected mixed $value,\n        protected bool $hit,\n        protected ?DateTimeInterface $expiration = null,\n    ) {\n    }\n\n    final public function get(): mixed\n    {\n        return $this->isHit() ? $this->value : null;\n    }\n\n    /**\n     * Returns the expiration timestamp.\n     */\n    final public function getExpiration(): DateTimeInterface\n    {\n        return $this->expiration ?? new DateTimeImmutable('now +1 year');\n    }\n\n    final public function getKey(): string\n    {\n        return $this->key;\n    }\n\n    /**\n     * Returns the raw value, regardless of hit status.\n     */\n    final public function getRawValue(): mixed\n    {\n        return $this->value;\n    }\n\n    final public function isHit(): bool\n    {\n        return $this->hit;\n    }\n\n    abstract public function expiresAfter(int | DateInterval | null $time): static;\n\n    abstract public function expiresAt(?DateTimeInterface $expiration): static;\n\n    abstract public function set(mixed $value): static;\n}\n"
  },
  {
    "path": "src/Bridges/CacheItemBridgeContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\nuse Psr\\Cache\\CacheItemInterface;\n\n/**\n * @api\n */\ninterface CacheItemBridgeContract extends BridgeContract, CacheItemInterface\n{\n    /**\n     * Return a LaravelCacheItem instance flagged as missed.\n     *\n     * @param string $key\n     */\n    public static function miss(string $key): self;\n}\n"
  },
  {
    "path": "src/Bridges/SessionBridge.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\n/**\n * Bridges the Laravel's Session API with the Auth0 PHP SDK's StoreInterface.\n *\n * @internal\n *\n * @api\n */\nfinal class SessionBridge extends SessionBridgeAbstract implements SessionBridgeContract\n{\n}\n"
  },
  {
    "path": "src/Bridges/SessionBridgeAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\nuse Auth0\\Laravel\\Exceptions\\SessionException;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Session\\Store;\nuse InvalidArgumentException;\n\nuse function array_key_exists;\nuse function is_array;\nuse function is_string;\n\n/**\n * @api\n */\nabstract class SessionBridgeAbstract extends BridgeAbstract\n{\n    public function __construct(\n        protected string $prefix = 'auth0',\n    ) {\n        $this->setPrefix($prefix);\n    }\n\n    /**\n     * This method is required by the interface but is not used by this SDK.\n     *\n     * @param bool $deferring whether to defer persisting the storage state\n     */\n    final public function defer(bool $deferring): void\n    {\n    }\n\n    /**\n     * Delete a value from the Laravel session by key. (Key will be automatically prefixed with the SDK's configured namespace.).\n     *\n     * @param string $key session key to delete\n     *\n     * @throws SessionException If a Laravel session store is not available.\n     */\n    final public function delete(string $key): void\n    {\n        $payload = $this->getPayload() ?? [];\n\n        if (array_key_exists($key, $payload)) {\n            unset($payload[$key]);\n            $this->getStore()->put($this->getPrefix(), json_encode(array_filter($payload), JSON_THROW_ON_ERROR));\n        }\n    }\n\n    /**\n     * Retrieve a value from the Laravel session by key. (Key will be automatically prefixed with the SDK's configured namespace.).\n     *\n     * @param string $key     session key to query\n     * @param mixed  $default default to return if nothing was found\n     *\n     * @throws SessionException If a Laravel session store is not available.\n     */\n    final public function get(string $key, $default = null): mixed\n    {\n        $payload = $this->getPayload() ?? [];\n\n        return $payload[$key] ?? $default;\n    }\n\n    /**\n     * Get all values from the Laravel session that are prefixed with the SDK's configured namespace.\n     *\n     * @throws SessionException If a Laravel session store is not available.\n     */\n    final public function getAll(): array\n    {\n        return $this->getPayload() ?? [];\n    }\n\n    /**\n     * Get the prefix used for all session keys.\n     *\n     * @return string Prefix used for all session keys.\n     */\n    final public function getPrefix(): string\n    {\n        return $this->prefix;\n    }\n\n    /**\n     * Delete all values from the Laravel session that are prefixed with the SDK's configured namespace.\n     *\n     * @throws SessionException If a Laravel session store is not available.\n     */\n    final public function purge(): void\n    {\n        $this->getStore()->forget($this->getPrefix());\n    }\n\n    /**\n     * Store a value in the Laravel session. (Key will be automatically prefixed with the SDK's configured namespace.).\n     *\n     * @param string $key   session key to set\n     * @param mixed  $value value to use\n     *\n     * @throws SessionException If a Laravel session store is not available.\n     */\n    final public function set(string $key, $value): void\n    {\n        $payload = $this->getPayload() ?? [];\n        $payload[$key] = $value;\n\n        $this->getStore()->put($this->getPrefix(), json_encode($payload, JSON_THROW_ON_ERROR));\n    }\n\n    /**\n     * Set the prefix used for all session keys.\n     *\n     * @param string $prefix Prefix to use for all session keys.\n     *\n     * @return $this\n     */\n    final public function setPrefix(\n        string $prefix = 'auth0',\n    ): self {\n        $prefix = trim($prefix);\n\n        if ('' === $prefix) {\n            throw new InvalidArgumentException('Prefix cannot be empty.');\n        }\n\n        $this->prefix = $prefix;\n\n        return $this;\n    }\n\n    protected function getPayload(): ?array\n    {\n        $encoded = $this->getStore()->get($this->getPrefix());\n\n        if (is_string($encoded)) {\n            $decoded = json_decode($encoded, true, 512);\n\n            if (is_array($decoded)) {\n                return $decoded;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Retrieves the Laravel session store.\n     *\n     * @throws SessionException If a Laravel session store is not available.\n     *\n     * @psalm-suppress RedundantConditionGivenDocblockType\n     */\n    protected function getStore(): Store\n    {\n        /**\n         * @var Store $store\n         */\n        $store = app('session.store');\n        /**\n         * @var Request $request\n         */\n        $request = app('request');\n\n        if (! $request->hasSession(true)) {\n            $request->setLaravelSession($store);\n        }\n\n        if (! $store->isStarted()) {\n            $store->start();\n        }\n\n        return $store;\n    }\n}\n"
  },
  {
    "path": "src/Bridges/SessionBridgeContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Bridges;\n\nuse Auth0\\SDK\\Contract\\StoreInterface;\n\n/**\n * @api\n */\ninterface SessionBridgeContract extends StoreInterface\n{\n}\n"
  },
  {
    "path": "src/Configuration.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Illuminate\\Support\\{Arr, Str};\n\nuse function constant;\nuse function count;\nuse function defined;\nuse function in_array;\nuse function is_array;\nuse function is_bool;\nuse function is_int;\nuse function is_string;\n\n/**\n * Helpers to map configuration data stored as strings from .env files into formats consumable by the Auth0-PHP SDK.\n *\n * @api\n */\nfinal class Configuration implements ConfigurationContract\n{\n    /**\n     * @var string[]\n     */\n    private const USES_ARRAYS = [\n        self::CONFIG_AUDIENCE,\n        self::CONFIG_SCOPE,\n        self::CONFIG_ORGANIZATION,\n    ];\n\n    /**\n     * @var string[]\n     */\n    private const USES_BOOLEANS = [\n        self::CONFIG_USE_PKCE,\n        self::CONFIG_HTTP_TELEMETRY,\n        self::CONFIG_COOKIE_SECURE,\n        self::CONFIG_PUSHED_AUTHORIZATION_REQUEST,\n    ];\n\n    /**\n     * @var string[]\n     */\n    private const USES_INTEGERS = [\n        self::CONFIG_TOKEN_MAX_AGE,\n        self::CONFIG_TOKEN_LEEWAY,\n        self::CONFIG_TOKEN_CACHE_TTL,\n        self::CONFIG_HTTP_MAX_RETRIES,\n        self::CONFIG_COOKIE_EXPIRES,\n        self::CONFIG_BACKCHANNEL_LOGOUT_EXPIRES,\n    ];\n\n    /**\n     * @var string\n     */\n    public const CONFIG_AUDIENCE = 'audience';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_BACKCHANNEL_LOGOUT_CACHE = 'backchannelLogoutCache';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_BACKCHANNEL_LOGOUT_EXPIRES = 'backchannelLogoutExpires';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_CLIENT_ASSERTION_SIGNING_ALGORITHM = 'clientAssertionSigningAlgorithm';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_CLIENT_ASSERTION_SIGNING_KEY = 'clientAssertionSigningKey';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_CLIENT_ID = 'clientId';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_CLIENT_SECRET = 'clientSecret';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_COOKIE_DOMAIN = 'cookieDomain';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_COOKIE_EXPIRES = 'cookieExpires';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_COOKIE_PATH = 'cookiePath';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_COOKIE_SAME_SITE = 'cookieSameSite';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_COOKIE_SECRET = 'cookieSecret';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_COOKIE_SECURE = 'cookieSecure';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_CUSTOM_DOMAIN = 'customDomain';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_DOMAIN = 'domain';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_HTTP_MAX_RETRIES = 'httpMaxRetries';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_HTTP_TELEMETRY = 'httpTelemetry';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_MANAGEMENT_TOKEN = 'managementToken';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_MANAGEMENT_TOKEN_CACHE = 'managementTokenCache';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_NAMESPACE = 'auth0.';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_NAMESPACE_ROUTES = 'auth0.routes.';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_ORGANIZATION = 'organization';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_PUSHED_AUTHORIZATION_REQUEST = 'pushedAuthorizationRequest';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_REDIRECT_URI = 'redirectUri';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_RESPONSE_MODE = 'responseMode';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_RESPONSE_TYPE = 'responseType';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_ROUTE_AFTER_LOGIN = 'afterLogin';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_ROUTE_AFTER_LOGOUT = 'afterLogout';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_ROUTE_BACKCHANNEL = 'backchannel';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_ROUTE_CALLBACK = 'callback';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_ROUTE_INDEX = 'index';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_ROUTE_LOGIN = 'login';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_ROUTE_LOGOUT = 'logout';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_SCOPE = 'scope';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_SESSION_STORAGE = 'sessionStorage';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_SESSION_STORAGE_ID = 'sessionStorageId';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_STRATEGY = 'strategy';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_TOKEN_ALGORITHM = 'tokenAlgorithm';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_TOKEN_CACHE = 'tokenCache';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_TOKEN_CACHE_TTL = 'tokenCacheTtl';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_TOKEN_JWKS_URI = 'tokenJwksUri';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_TOKEN_LEEWAY = 'tokenLeeway';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_TOKEN_MAX_AGE = 'tokenMaxAge';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_TRANSIENT_STORAGE = 'transientStorage';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_TRANSIENT_STORAGE_ID = 'transientStorageId';\n\n    /**\n     * @var string\n     */\n    public const CONFIG_USE_PKCE = 'usePkce';\n\n    /**\n     * @var array<string, int>\n     */\n    public const VERSION_2 = ['AUTH0_CONFIG_VERSION' => 2];\n\n    private static ?array $environment = null;\n\n    private static ?array $json = null;\n\n    private static ?string $path = null;\n\n    public static function get(\n        string $setting,\n        array | string | int | bool | null $default = null,\n    ): array | string | int | bool | null {\n        if (in_array($setting, self::USES_ARRAYS, true)) {\n            $value = self::getValue($setting, $default);\n\n            if (! is_string($value)) {\n                return $default;\n            }\n\n            return self::stringToArrayOrNull($value, ',') ?? $default;\n        }\n\n        if (in_array($setting, self::USES_BOOLEANS, true)) {\n            $value = self::getValue($setting, $default);\n\n            if (! is_bool($value) && ! is_string($value)) {\n                return $default;\n            }\n\n            return self::stringToBoolOrNull($value) ?? $default;\n        }\n\n        if (in_array($setting, self::USES_INTEGERS, true)) {\n            $value = self::getValue($setting, $default);\n\n            if (! is_int($value) && ! is_string($value)) {\n                return $default;\n            }\n\n            return self::stringOrIntToIntOrNull($value) ?? $default;\n        }\n\n        $result = null;\n        $value = self::getValue($setting) ?? $default;\n\n        if (is_string($value) || is_int($value) || null === $value) {\n            $result = self::stringOrNull($value) ?? $default;\n        }\n\n        if (self::CONFIG_DOMAIN === $setting && null === $result) {\n            // Fallback to extracting the tenant domain from the signing key subject.\n            $temp = self::getJson()['signing_keys.0.subject'] ?? '';\n            $temp = explode('=', $temp);\n\n            if (isset($temp[1]) && str_ends_with($temp[1], '.auth0.com')) {\n                $result = $temp[1]; // @codeCoverageIgnore\n            }\n        }\n\n        return $result ?? $default;\n    }\n\n    /**\n     * @codeCoverageIgnore\n     *\n     * @psalm-suppress DocblockTypeContradiction\n     */\n    public static function getEnvironment(): array\n    {\n        if (null === self::$environment) {\n            $path = self::getPath();\n            $laravelEnvironment = env('APP_ENV');\n            $laravelEnvironment = is_string($laravelEnvironment) && '' !== trim($laravelEnvironment) ? trim($laravelEnvironment) : 'local';\n\n            $env = [];\n            $files = ['.env', '.env.auth0'];\n            $files[] = '.env.' . $laravelEnvironment;\n            $files[] = '.env.auth0.' . $laravelEnvironment;\n\n            foreach ($files as $file) {\n                if (! file_exists($path . $file)) {\n                    continue;\n                }\n\n                $contents = file($path . $file, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);\n\n                if (! is_array($contents)) {\n                    continue;\n                }\n\n                if ([] === $contents) {\n                    continue;\n                }\n\n                foreach ($contents as $content) {\n                    if (1 !== substr_count($content, '=')) {\n                        continue;\n                    }\n\n                    [$k,$v] = explode('=', $content);\n\n                    // @phpstan-ignore-next-line\n                    if (! is_string($k)) {\n                        continue;\n                    }\n\n                    // @phpstan-ignore-next-line\n                    if (! is_string($v)) {\n                        continue;\n                    }\n\n                    $v = trim($v);\n\n                    if ('' === $v) {\n                        $v = null;\n                    } elseif ('empty' === $v) {\n                        $v = null;\n                    } elseif ('(empty)' === $v) {\n                        $v = null;\n                    } elseif ('null' === $v) {\n                        $v = null;\n                    } elseif ('(null)' === $v) {\n                        $v = null;\n                    } elseif ('true' === $v) {\n                        $v = true;\n                    } elseif ('(true)' === $v) {\n                        $v = true;\n                    } elseif ('false' === $v) {\n                        $v = false;\n                    } elseif ('(false)' === $v) {\n                        $v = false;\n                    }\n\n                    $env[trim($k)] = $v;\n                }\n            }\n\n            self::$environment = $env;\n        }\n\n        return self::$environment;\n    }\n\n    /**\n     * @codeCoverageIgnore\n     */\n    public static function getJson(): array\n    {\n        if (null === self::$json) {\n            $path = self::getPath();\n            $laravelEnvironment = env('APP_ENV');\n            $laravelEnvironment = is_string($laravelEnvironment) && '' !== trim($laravelEnvironment) ? trim($laravelEnvironment) : 'local';\n\n            $configuration = [];\n            $files = ['.auth0.json', '.auth0.api.json', '.auth0.app.json'];\n            $files[] = '.auth0.' . $laravelEnvironment . '.json';\n            $files[] = '.auth0.' . $laravelEnvironment . '.api.json';\n            $files[] = '.auth0.' . $laravelEnvironment . '.app.json';\n\n            foreach ($files as $file) {\n                if (file_exists($path . $file)) {\n                    $content = file_get_contents($path . $file);\n\n                    if (is_string($content)) {\n                        $json = json_decode($content, true, 512);\n\n                        if (is_array($json)) {\n                            $configuration = array_merge($configuration, $json);\n                        }\n                    }\n                }\n            }\n\n            self::$json = Arr::dot($configuration);\n        }\n\n        return self::$json;\n    }\n\n    public static function getPath(): string\n    {\n        if (null === self::$path) {\n            $path = config('auth0.configurationPath');\n\n            if (! is_string($path)) {\n                $path = base_path() . DIRECTORY_SEPARATOR;\n            }\n\n            self::$path = $path;\n        }\n\n        return self::$path;\n    }\n\n    public static function string(string $key, ?string $default = null): ?string\n    {\n        $value = config($key, $default);\n\n        if (is_string($value)) {\n            return $value;\n        }\n\n        return null;\n    }\n\n    public static function stringOrIntToIntOrNull(\n        int | string $value,\n        int | null $default = null,\n    ): int | null {\n        if (is_int($value)) {\n            return $value;\n        }\n\n        $value = trim($value);\n\n        if ('' === $value) {\n            return $default;\n        }\n\n        if (ctype_digit($value)) {\n            return (int) $value;\n        }\n\n        return $default;\n    }\n\n    public static function stringOrNull(\n        string | int | null $value,\n        string | int | null $default = null,\n    ): string | int | null {\n        if (! is_string($value)) {\n            return $default;\n        }\n\n        $value = trim($value);\n\n        if ('' === $value) {\n            return $default;\n        }\n\n        if ('empty' === $value) {\n            return $default;\n        }\n\n        if ('(empty)' === $value) {\n            return $default;\n        }\n\n        if ('null' === $value) {\n            return $default;\n        }\n\n        if ('(null)' === $value) {\n            return $default;\n        }\n\n        return $value;\n    }\n\n    public static function stringToArray(array | string | null $config, string $delimiter = ' '): array\n    {\n        if (is_array($config)) {\n            return $config;\n        }\n\n        if (is_string($config) && '' !== $config && '' !== $delimiter) {\n            $response = explode($delimiter, $config);\n\n            // @phpstan-ignore-next-line\n            if (count($response) >= 1 && '' !== trim($response[0])) {\n                return $response;\n            }\n        }\n\n        return [];\n    }\n\n    public static function stringToArrayOrNull(array | string | null $config, string $delimiter = ' '): ?array\n    {\n        if (is_array($config) && [] !== $config) {\n            return $config;\n        }\n\n        if (is_string($config) && '' !== $config && '' !== $delimiter) {\n            $response = explode($delimiter, $config);\n\n            // @phpstan-ignore-next-line\n            if (count($response) >= 1 && '' !== trim($response[0])) {\n                return $response;\n            }\n        }\n\n        return null;\n    }\n\n    public static function stringToBoolOrNull(bool | string | null $config, ?bool $default = null): ?bool\n    {\n        if (is_bool($config)) {\n            return $config;\n        }\n\n        if (is_string($config) && '' !== $config) {\n            $config = strtolower(trim($config));\n\n            if ('true' === $config) {\n                return true;\n            }\n\n            if ('false' === $config) {\n                return false;\n            }\n        }\n\n        return $default;\n    }\n\n    public static function version(): int\n    {\n        $version = config('auth0.AUTH0_CONFIG_VERSION', 1);\n\n        return is_int($version) ? $version : 1;\n    }\n\n    private static function getValue(\n        string $setting,\n        array | bool | string | int | null $default = null,\n    ): array | bool | string | int | null {\n        $value = null;\n\n        if (defined('AUTH0_OVERRIDE_CONFIGURATION')) {\n            $override = constant('AUTH0_OVERRIDE_CONFIGURATION');\n\n            if (is_string($override)) {\n                $value = config($override . '.' . $setting);\n            }\n        } else {\n            $env = 'AUTH0_' . strtoupper(Str::snake($setting));\n            $json = self::CONFIG_AUDIENCE === $setting ? 'identifier' : Str::snake($setting);\n\n            $value = getenv($env);\n\n            if (! is_string($value)) {\n                $value = null;\n            }\n\n            $value ??= self::getEnvironment()[$env] ?? null;\n\n            $value = match ($setting) {\n                self::CONFIG_DOMAIN => '{DOMAIN}' === $value ? null : $value,\n                self::CONFIG_CLIENT_ID => '{CLIENT_ID}' === $value ? null : $value,\n                self::CONFIG_CLIENT_SECRET => '{CLIENT_SECRET}' === $value ? null : $value,\n                self::CONFIG_AUDIENCE => '{API_IDENTIFIER}' === $value ? null : $value,\n                default => $value,\n            };\n\n            $value ??= self::getJson()[$json] ?? $default;\n        }\n\n        if (! is_string($value) && ! is_array($value) && ! is_bool($value) && ! is_int($value)) {\n            $value = null;\n        }\n\n        if (is_string($value)) {\n            $value = trim($value, '\\'\"');\n        }\n\n        return $value ?? $default;\n    }\n}\n"
  },
  {
    "path": "src/ConfigurationContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\ninterface ConfigurationContract\n{\n    public static function get(\n        string $setting,\n        array | string | int | bool | null $default = null,\n    ): array | string | int | bool | null;\n\n    public static function getEnvironment(): array;\n\n    public static function getJson(): array;\n\n    public static function getPath(): string;\n\n    public static function stringOrIntToIntOrNull(\n        int | string $value,\n        int | null $default = null,\n    ): int | null;\n\n    public static function stringOrNull(\n        int | string | null $value,\n        string | int | null $default = null,\n    ): string | int | null;\n\n    public static function stringToArray(array | string | null $config, string $delimiter = ' '): array;\n\n    /**\n     * Converts a delimited string into an array, or null, if nothing was provided.\n     *\n     * @param null|array<string>|string $config\n     * @param string                    $delimiter\n     */\n    public static function stringToArrayOrNull(array | string | null $config, string $delimiter = ' '): ?array;\n\n    /**\n     * Converts a truthy string representation into a boolean.\n     *\n     * @param null|bool|string $config\n     * @param ?bool            $default\n     */\n    public static function stringToBoolOrNull(string | bool | null $config, ?bool $default = null): ?bool;\n}\n"
  },
  {
    "path": "src/Controllers/CallbackController.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\n/**\n * Controller for handling a callback request, after a user is returned from authenticating with Auth0.\n *\n * @api\n */\nfinal class CallbackController extends CallbackControllerAbstract implements CallbackControllerContract\n{\n}\n"
  },
  {
    "path": "src/Controllers/CallbackControllerAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\{Configuration, Events};\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\Laravel\\Events\\{AuthenticationFailed, AuthenticationSucceeded};\nuse Auth0\\Laravel\\Exceptions\\ControllerException;\nuse Auth0\\Laravel\\Exceptions\\Controllers\\CallbackControllerException;\nuse Auth0\\Laravel\\Guards\\GuardAbstract;\nuse Illuminate\\Auth\\Events\\{Attempting, Authenticated, Failed, Validated};\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\nuse Throwable;\n\nuse function is_string;\nuse function sprintf;\n\n/**\n * @api\n */\nabstract class CallbackControllerAbstract extends ControllerAbstract\n{\n    /**\n     * @psalm-suppress InvalidArgument\n     *\n     * @param Request $request\n     */\n    public function __invoke(\n        Request $request,\n    ): Response {\n        $guard = auth()->guard();\n\n        if (! $guard instanceof GuardAbstract) {\n            logger()->error(sprintf('A request implementing the `%s` controller was not routed through a Guard configured with an Auth0 driver. The incorrectly assigned Guard was: %s', self::class, $guard::class), $request->toArray());\n\n            throw new ControllerException(ControllerException::ROUTED_USING_INCOMPATIBLE_GUARD);\n        }\n\n        $code = $request->query('code');\n        $state = $request->query('state');\n        $code = is_string($code) ? trim($code) : '';\n        $state = is_string($state) ? trim($state) : '';\n        $success = false;\n\n        if ('' === $code) {\n            $code = null;\n        }\n\n        if ('' === $state) {\n            $state = null;\n        }\n\n        /**\n         * @var null|string $code\n         * @var null|string $state\n         */\n        try {\n            if (null !== $code && null !== $state) {\n                Events::framework(new Attempting($guard::class, ['code' => $code, 'state' => $state], true));\n\n                $success = $guard->sdk()->exchange(\n                    code: $code,\n                    state: $state,\n                );\n            }\n        } catch (Throwable $throwable) {\n            $credentials = $guard->sdk()->getUser() ?? [];\n            $credentials['code'] = $code;\n            $credentials['state'] = $state;\n            $credentials['error'] = ['description' => $throwable->getMessage()];\n\n            Events::framework(new Failed($guard::class, $guard->user(), $credentials));\n\n            session()->invalidate();\n\n            Events::dispatch($event = new AuthenticationFailed($throwable, true));\n\n            if ($event->throwException) {\n                throw $throwable;\n            }\n        }\n\n        if (null !== $request->query('error') && null !== $request->query('error_description')) {\n            // Workaround to aid static analysis, due to the mixed formatting of the query() response:\n            $error = $request->query('error', '');\n            $errorDescription = $request->query('error_description', '');\n            $error = is_string($error) ? $error : '';\n            $errorDescription = is_string($errorDescription) ? $errorDescription : '';\n\n            Events::framework(new Attempting($guard::class, ['code' => $code, 'state' => $state], true));\n\n            Events::framework(new Failed($guard::class, $guard->user(), [\n                'code' => $code,\n                'state' => $state,\n                'error' => ['error' => $error, 'description' => $errorDescription],\n            ]));\n\n            session()->invalidate();\n\n            // Create a dynamic exception to report the API error response\n            $exception = new CallbackControllerException(sprintf(CallbackControllerException::MSG_API_RESPONSE, $error, $errorDescription));\n\n            // Store the API exception in the session as a flash variable, in case the application wants to access it.\n            session()->flash('auth0.callback.error', sprintf(CallbackControllerException::MSG_API_RESPONSE, $error, $errorDescription));\n\n            Events::dispatch($event = new AuthenticationFailed($exception, true));\n\n            if ($event->throwException) {\n                throw $exception;\n            }\n        }\n\n        if (! $success) {\n            return redirect()->intended(config(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_LOGIN, '/login'));\n        }\n\n        $credential = ($guard instanceof Guard) ? $guard->find(Guard::SOURCE_SESSION) : $guard->find();\n\n        $user = $credential?->getUser();\n\n        if ($credential instanceof CredentialEntityContract && $user instanceof Authenticatable) {\n            Events::framework(new Validated($guard::class, $user));\n\n            session()->regenerate(true);\n\n            /**\n             * @var Guard $guard\n             */\n            $guard->login($credential, Guard::SOURCE_SESSION);\n\n            Events::dispatch(new AuthenticationSucceeded($user));\n\n            // @phpstan-ignore-next-line\n            if ($user instanceof Authenticatable) {\n                Events::framework(new Authenticated($guard::class, $user));\n            }\n        }\n\n        return redirect()->intended(config(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_AFTER_LOGIN, config(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_INDEX, '/')));\n    }\n}\n"
  },
  {
    "path": "src/Controllers/CallbackControllerContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\ninterface CallbackControllerContract extends ControllerContract\n{\n    /**\n     * Process the session for the end user after returning from authenticating with Auth0.\n     *\n     * @param Request $request the incoming request instance\n     */\n    public function __invoke(Request $request): Response;\n}\n"
  },
  {
    "path": "src/Controllers/ControllerAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Routing\\Controller;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * Base controller for SDK controllers.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\nabstract class ControllerAbstract extends Controller\n{\n    abstract public function __invoke(\n        Request $request,\n    ): Response;\n}\n"
  },
  {
    "path": "src/Controllers/ControllerContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\n/**\n * @api\n */\ninterface ControllerContract\n{\n}\n"
  },
  {
    "path": "src/Controllers/LoginController.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\n/**\n * Controller for handling a login request.\n *\n * @api\n */\nfinal class LoginController extends LoginControllerAbstract implements LoginControllerContract\n{\n}\n"
  },
  {
    "path": "src/Controllers/LoginControllerAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\{Configuration, Events};\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\Laravel\\Events\\LoginAttempting;\nuse Auth0\\Laravel\\Exceptions\\ControllerException;\nuse Auth0\\Laravel\\Guards\\GuardAbstract;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nuse function sprintf;\n\n/**\n * Controller for handling a login request.\n *\n * @api\n */\nabstract class LoginControllerAbstract extends ControllerAbstract\n{\n    /**\n     * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter\n     *\n     * @param Request $request\n     */\n    public function __invoke(\n        Request $request,\n    ): Response {\n        $guard = auth()->guard();\n\n        if (! $guard instanceof GuardAbstract) {\n            logger()->error(sprintf('A request implementing the `%s` controller was not routed through a Guard configured with an Auth0 driver. The incorrectly assigned Guard was: %s', self::class, $guard::class), $request->toArray());\n\n            throw new ControllerException(ControllerException::ROUTED_USING_INCOMPATIBLE_GUARD);\n        }\n\n        $loggedIn = $guard->check() ? true : null;\n        $loggedIn ??= (($guard instanceof Guard) ? $guard->find(Guard::SOURCE_SESSION) : $guard->find()) instanceof CredentialEntityContract;\n\n        if ($loggedIn) {\n            return redirect()->intended(\n                config(\n                    Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_AFTER_LOGIN,\n                    config(\n                        Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_INDEX,\n                        '/',\n                    ),\n                ),\n            );\n        }\n\n        session()->regenerate(true);\n\n        Events::dispatch($event = new LoginAttempting());\n\n        $url = $guard->sdk()->login(params: $event->parameters);\n\n        return redirect()->away($url);\n    }\n}\n"
  },
  {
    "path": "src/Controllers/LoginControllerContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\ninterface LoginControllerContract extends ControllerContract\n{\n    /**\n     * Redirect to the configured Auth0 Universal Login Page if a session is not available.\n     * Otherwise, redirect to the \"/\" route.\n     *\n     * @param Request $request the incoming request instance\n     */\n    public function __invoke(Request $request): Response;\n}\n"
  },
  {
    "path": "src/Controllers/LogoutController.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\n/**\n * Controller for handling a logout request.\n *\n * @api\n */\nfinal class LogoutController extends LogoutControllerAbstract implements LogoutControllerContract\n{\n}\n"
  },
  {
    "path": "src/Controllers/LogoutControllerAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\Configuration;\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\Laravel\\Exceptions\\ControllerException;\nuse Auth0\\Laravel\\Guards\\GuardAbstract;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nuse function sprintf;\n\n/**\n * Controller for handling a logout request.\n *\n * @api\n */\nabstract class LogoutControllerAbstract extends ControllerAbstract\n{\n    /**\n     * @psalm-suppress RedundantCastGivenDocblockType\n     *\n     * @param Request $request\n     */\n    public function __invoke(\n        Request $request,\n    ): Response {\n        $guard = auth()->guard();\n\n        if (! $guard instanceof GuardAbstract) {\n            logger()->error(sprintf('A request implementing the `%s` controller was not routed through a Guard configured with an Auth0 driver. The incorrectly assigned Guard was: %s', self::class, $guard::class), $request->toArray());\n\n            throw new ControllerException(ControllerException::ROUTED_USING_INCOMPATIBLE_GUARD);\n        }\n\n        $loggedIn = $guard->check() ? true : null;\n        $loggedIn ??= (($guard instanceof Guard) ? $guard->find(Guard::SOURCE_SESSION) : $guard->find()) instanceof CredentialEntityContract;\n\n        $landing = Configuration::string(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_AFTER_LOGOUT);\n        $landing ??= Configuration::string(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_INDEX);\n        $landing ??= '/';\n\n        if ($loggedIn) {\n            session()->invalidate();\n\n            $guard->logout(); /** @phpstan-ignore-line */\n            $route = (string) url($landing); /** @phpstan-ignore-line */\n            $url = $guard->sdk()->authentication()->getLogoutLink($route);\n\n            return redirect()->away($url);\n        }\n\n        return redirect()->intended($landing);\n    }\n}\n"
  },
  {
    "path": "src/Controllers/LogoutControllerContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Controllers;\n\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\ninterface LogoutControllerContract extends ControllerContract\n{\n    /**\n     * Redirect to Auth0's logout endpoint if a session is available.\n     * Otherwise, redirect to the \"/\" route.\n     *\n     * @param Request $request the incoming request instance\n     */\n    public function __invoke(Request $request): Response;\n}\n"
  },
  {
    "path": "src/Entities/CredentialEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Entities;\n\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse JsonSerializable;\n\n/**\n * An entity representing a user credential.\n *\n * @internal\n *\n * @api\n */\nfinal class CredentialEntity extends CredentialEntityAbstract implements CredentialEntityContract, JsonSerializable\n{\n    public function clear(): self\n    {\n        $this->user = null;\n        $this->idToken = null;\n        $this->accessToken = null;\n        $this->accessTokenDecoded = null;\n        $this->accessTokenScope = null;\n        $this->accessTokenExpiration = null;\n        $this->refreshToken = null;\n\n        return $this;\n    }\n\n    public function setAccessToken(\n        ?string $accessToken = null,\n    ): self {\n        $this->accessToken = $accessToken;\n\n        return $this;\n    }\n\n    public function setAccessTokenDecoded(\n        ?array $accessTokenDecoded = null,\n    ): self {\n        $this->accessTokenDecoded = $accessTokenDecoded;\n\n        return $this;\n    }\n\n    public function setAccessTokenExpiration(\n        ?int $accessTokenExpiration = null,\n    ): self {\n        $this->accessTokenExpiration = $accessTokenExpiration;\n\n        return $this;\n    }\n\n    public function setAccessTokenScope(\n        ?array $accessTokenScope = null,\n    ): self {\n        $this->accessTokenScope = $accessTokenScope;\n\n        return $this;\n    }\n\n    public function setIdToken(\n        ?string $idToken = null,\n    ): self {\n        $this->idToken = $idToken;\n\n        return $this;\n    }\n\n    public function setRefreshToken(\n        ?string $refreshToken = null,\n    ): self {\n        $this->refreshToken = $refreshToken;\n\n        return $this;\n    }\n\n    public function setUser(\n        ?Authenticatable $user = null,\n    ): self {\n        $this->user = $user;\n\n        return $this;\n    }\n\n    /**\n     * Create a new Credential instance.\n     *\n     * @param null|Authenticatable $user                  The user entity this credential represents.\n     * @param null|string          $idToken               The ID token for this credential.\n     * @param null|string          $accessToken           The access token for this credential.\n     * @param null|array<string>   $accessTokenScope      The access token scope for this credential.\n     * @param null|int             $accessTokenExpiration The access token expiration for this credential.\n     * @param null|string          $refreshToken          The refresh token for this credential.\n     * @param null|array<string>   $accessTokenDecoded    The decoded access token for this credential.\n     */\n    public static function create(\n        ?Authenticatable $user = null,\n        ?string $idToken = null,\n        ?string $accessToken = null,\n        ?array $accessTokenScope = null,\n        ?int $accessTokenExpiration = null,\n        ?string $refreshToken = null,\n        ?array $accessTokenDecoded = null,\n    ): self {\n        return new self(\n            $user,\n            $idToken,\n            $accessToken,\n            $accessTokenScope,\n            $accessTokenExpiration,\n            $refreshToken,\n            $accessTokenDecoded,\n        );\n    }\n}\n"
  },
  {
    "path": "src/Entities/CredentialEntityAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Entities;\n\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\n/**\n * @api\n */\nabstract class CredentialEntityAbstract extends EntityAbstract\n{\n    /**\n     * @param null|Authenticatable $user                  The user entity this credential represents.\n     * @param null|string          $idToken               The ID token for this credential.\n     * @param null|string          $accessToken           The access token for this credential.\n     * @param null|array<mixed>    $accessTokenScope      The access token scope for this credential.\n     * @param null|int             $accessTokenExpiration The access token expiration for this credential.\n     * @param null|string          $refreshToken          The refresh token for this credential.\n     * @param null|array<mixed>    $accessTokenDecoded    The decoded access token for this credential.\n     */\n    public function __construct(\n        protected ?Authenticatable $user = null,\n        protected ?string $idToken = null,\n        protected ?string $accessToken = null,\n        protected ?array $accessTokenScope = null,\n        protected ?int $accessTokenExpiration = null,\n        protected ?string $refreshToken = null,\n        protected ?array $accessTokenDecoded = null,\n    ) {\n    }\n\n    final public function getAccessToken(): ?string\n    {\n        return $this->accessToken;\n    }\n\n    /**\n     * @psalm-suppress MixedReturnTypeCoercion\n     */\n    final public function getAccessTokenDecoded(): ?array\n    {\n        return $this->accessTokenDecoded;\n    }\n\n    final public function getAccessTokenExpiration(): ?int\n    {\n        return $this->accessTokenExpiration;\n    }\n\n    final public function getAccessTokenExpired(): ?bool\n    {\n        $expires = $this->getAccessTokenExpiration();\n\n        if (null === $expires || $expires <= 0) {\n            return null;\n        }\n\n        return time() >= $expires;\n    }\n\n    /**\n     * @psalm-suppress MixedReturnTypeCoercion\n     */\n    final public function getAccessTokenScope(): ?array\n    {\n        return $this->accessTokenScope;\n    }\n\n    final public function getIdToken(): ?string\n    {\n        return $this->idToken;\n    }\n\n    final public function getRefreshToken(): ?string\n    {\n        return $this->refreshToken;\n    }\n\n    final public function getUser(): ?Authenticatable\n    {\n        return $this->user;\n    }\n\n    /**\n     * @return array{user: false|string, idToken: null|string, accessToken: null|string, accessTokenDecoded: null|array<mixed>, accessTokenScope: null|array<mixed>, accessTokenExpiration: null|int, accessTokenExpired: null|bool, refreshToken: null|string}\n     */\n    final public function jsonSerialize(): mixed\n    {\n        return [\n            'user' => json_encode($this->getUser(), JSON_FORCE_OBJECT),\n            'idToken' => $this->getIdToken(),\n            'accessToken' => $this->getAccessToken(),\n            'accessTokenDecoded' => $this->getAccessTokenDecoded(),\n            'accessTokenScope' => $this->getAccessTokenScope(),\n            'accessTokenExpiration' => $this->getAccessTokenExpiration(),\n            'accessTokenExpired' => $this->getAccessTokenExpired(),\n            'refreshToken' => $this->getRefreshToken(),\n        ];\n    }\n\n    abstract public function clear(): self;\n\n    abstract public function setAccessToken(\n        ?string $accessToken = null,\n    ): self;\n\n    abstract public function setAccessTokenDecoded(\n        ?array $accessTokenDecoded = null,\n    ): self;\n\n    abstract public function setAccessTokenExpiration(\n        ?int $accessTokenExpiration = null,\n    ): self;\n\n    abstract public function setAccessTokenScope(\n        ?array $accessTokenScope = null,\n    ): self;\n\n    abstract public function setIdToken(\n        ?string $idToken = null,\n    ): self;\n\n    abstract public function setRefreshToken(\n        ?string $refreshToken = null,\n    ): self;\n\n    abstract public function setUser(\n        ?Authenticatable $user = null,\n    ): self;\n}\n"
  },
  {
    "path": "src/Entities/CredentialEntityContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Entities;\n\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\n/**\n * @api\n */\ninterface CredentialEntityContract extends EntityContract\n{\n    /**\n     * Clear all values from this Credential instance.\n     */\n    public function clear(): self;\n\n    /**\n     * Get the access token for this credential.\n     */\n    public function getAccessToken(): ?string;\n\n    /**\n     * Get the decoded access token content for this credential.\n     *\n     * @return null|array<mixed>\n     */\n    public function getAccessTokenDecoded(): ?array;\n\n    /**\n     * Get the access token expiration for this credential.\n     */\n    public function getAccessTokenExpiration(): ?int;\n\n    /**\n     * Check if the access token for this credential has expired.\n     */\n    public function getAccessTokenExpired(): ?bool;\n\n    /**\n     * Get the access token scope for this credential.\n     *\n     * @return null|array<mixed>\n     */\n    public function getAccessTokenScope(): ?array;\n\n    /**\n     * Get the ID token for this credential.\n     */\n    public function getIdToken(): ?string;\n\n    /**\n     * Get the refresh token for this credential.\n     */\n    public function getRefreshToken(): ?string;\n\n    /**\n     * Get the user entity this credential represents.\n     */\n    public function getUser(): ?Authenticatable;\n\n    /**\n     * Set the access token for this credential.\n     *\n     * @param null|string $accessToken The access token for this credential.\n     */\n    public function setAccessToken(\n        ?string $accessToken = null,\n    ): self;\n\n    /**\n     * Set the decoded access token content for this credential.\n     *\n     * @param null|array<mixed> $accessTokenDecoded The decoded access token content for this credential.\n     */\n    public function setAccessTokenDecoded(\n        ?array $accessTokenDecoded = null,\n    ): self;\n\n    /**\n     * Set the access token expiration for this credential.\n     *\n     * @param null|int $accessTokenExpiration The access token expiration for this credential.\n     */\n    public function setAccessTokenExpiration(\n        ?int $accessTokenExpiration = null,\n    ): self;\n\n    /**\n     * Set the access token scope for this credential.\n     *\n     * @param null|array<mixed> $accessTokenScope The access token scope for this credential.\n     */\n    public function setAccessTokenScope(\n        ?array $accessTokenScope = null,\n    ): self;\n\n    /**\n     * Set the ID token for this credential.\n     *\n     * @param null|string $idToken The ID token for this credential.\n     */\n    public function setIdToken(\n        ?string $idToken = null,\n    ): self;\n\n    /**\n     * Set the refresh token for this credential.\n     *\n     * @param null|string $refreshToken The refresh token for this credential.\n     */\n    public function setRefreshToken(\n        ?string $refreshToken = null,\n    ): self;\n\n    /**\n     * Set the user entity this credential represents.\n     *\n     * @param null|Authenticatable $user The user entity this credential represents.\n     */\n    public function setUser(\n        ?Authenticatable $user = null,\n    ): self;\n}\n"
  },
  {
    "path": "src/Entities/EntityAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Entities;\n\n/**\n * @api\n */\nabstract class EntityAbstract\n{\n}\n"
  },
  {
    "path": "src/Entities/EntityContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Entities;\n\n/**\n * @api\n */\ninterface EntityContract\n{\n}\n"
  },
  {
    "path": "src/Entities/InstanceEntity.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Entities;\n\n/**\n * An entity representing an instance of the Auth0 PHP SDK.\n *\n * @internal\n *\n * @api\n */\nfinal class InstanceEntity extends InstanceEntityAbstract implements InstanceEntityContract\n{\n    use InstanceEntityTrait;\n}\n"
  },
  {
    "path": "src/Entities/InstanceEntityAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Entities;\n\nuse Auth0\\Laravel\\Bridges\\{CacheBridge, SessionBridge};\nuse Auth0\\Laravel\\{Configuration, Events, Service};\nuse Auth0\\Laravel\\Events\\Configuration\\{BuildingConfigurationEvent, BuiltConfigurationEvent};\nuse Auth0\\SDK\\Auth0;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Contract\\API\\ManagementInterface;\nuse Auth0\\SDK\\Contract\\{Auth0Interface, StoreInterface};\nuse Auth0\\SDK\\Utility\\HttpTelemetry;\nuse Psr\\Cache\\CacheItemPoolInterface;\n\nuse function in_array;\nuse function is_array;\nuse function is_string;\n\n/**\n * @api\n */\nabstract class InstanceEntityAbstract extends EntityAbstract\n{\n    public function __construct(\n        protected ?Auth0Interface $sdk = null,\n        protected ?SdkConfiguration $configuration = null,\n        protected ?CacheItemPoolInterface $tokenCachePool = null,\n        protected ?CacheItemPoolInterface $managementTokenCachePool = null,\n        protected ?string $guardConfigurationKey = null,\n        protected ?CacheItemPoolInterface $backchannelLogoutCachePool = null,\n    ) {\n    }\n\n    final public function getConfiguration(): SdkConfiguration\n    {\n        if (! $this->configuration instanceof SdkConfiguration) {\n            $configuration = [];\n\n            if (2 === Configuration::version()) {\n                $defaultConfiguration = config('auth0.guards.default');\n                $guardConfiguration = [];\n\n                if (null !== $this->guardConfigurationKey && '' !== $this->guardConfigurationKey && 'default' !== $this->guardConfigurationKey) {\n                    $guardConfiguration = config('auth0.guards.' . $this->guardConfigurationKey) ?? [];\n                }\n\n                if (is_array($defaultConfiguration) && [] !== $defaultConfiguration) {\n                    $configuration = array_merge($configuration, array_filter($defaultConfiguration));\n                }\n\n                if (is_array($guardConfiguration) && [] !== $guardConfiguration) {\n                    $configuration = array_merge($configuration, array_filter($guardConfiguration));\n                }\n            }\n\n            if (2 !== Configuration::version()) {\n                $configuration = config('auth0');\n\n                if (! is_array($configuration)) {\n                    $configuration = [];\n                }\n            }\n\n            $this->configuration = $this->createConfiguration($configuration);\n        }\n\n        return $this->configuration;\n    }\n\n    final public function getCredentials(): ?object\n    {\n        return $this->getSdk()->getCredentials();\n    }\n\n    final public function getGuardConfigurationKey(): ?string\n    {\n        return $this->guardConfigurationKey;\n    }\n\n    final public function getSdk(): Auth0Interface\n    {\n        if (! $this->sdk instanceof Auth0Interface) {\n            return $this->setSdk(new Auth0($this->getConfiguration()));\n        }\n\n        return $this->sdk;\n    }\n\n    final public function management(): ManagementInterface\n    {\n        return $this->getSdk()->management();\n    }\n\n    final public function setGuardConfigurationKey(\n        ?string $guardConfigurationKey = null,\n    ): self {\n        $this->guardConfigurationKey = $guardConfigurationKey;\n\n        return $this;\n    }\n\n    final public function setSdk(Auth0Interface $sdk): Auth0Interface\n    {\n        $this->configuration = $sdk->configuration();\n        $this->sdk = $sdk;\n\n        $this->setSdkTelemetry();\n\n        return $this->sdk;\n    }\n\n    abstract public function reset(): self;\n\n    /**\n     * @param null|array<string>|SdkConfiguration $configuration\n     */\n    abstract public function setConfiguration(\n        SdkConfiguration | array | null $configuration = null,\n    ): self;\n\n    protected function bootBackchannelLogoutCache(array $config): array\n    {\n        $backchannelLogoutCache = $config['backchannelLogoutCache'] ?? null;\n\n        if (false === $backchannelLogoutCache) {\n            unset($config['backchannelLogoutCache']);\n\n            return $config;\n        }\n\n        if (null === $backchannelLogoutCache) {\n            $backchannelLogoutCache = $this->getBackchannelLogoutCachePool();\n        }\n\n        if (is_string($backchannelLogoutCache)) {\n            $backchannelLogoutCache = app(trim($backchannelLogoutCache));\n        }\n\n        $config['backchannelLogoutCache'] = $backchannelLogoutCache instanceof CacheItemPoolInterface ? $backchannelLogoutCache : null;\n\n        return $config;\n    }\n\n    protected function bootManagementTokenCache(array $config): array\n    {\n        $managementTokenCache = $config['managementTokenCache'] ?? null;\n        $this->getManagementTokenCachePool();\n\n        // if (false === $managementTokenCache) {\n        //     unset($config['managementTokenCache']);\n\n        //     return $config;\n        // }\n\n        // if (null === $managementTokenCache) {\n        //     $managementTokenCache = $this->getManagementTokenCachePool();\n        // }\n\n        // if (is_string($managementTokenCache)) {\n        //     $managementTokenCache = app(trim($managementTokenCache));\n        // }\n\n        $config['managementTokenCache'] = $managementTokenCache instanceof CacheItemPoolInterface ? $managementTokenCache : null;\n\n        return $config;\n    }\n\n    protected function bootSessionStorage(array $config): array\n    {\n        $sessionStorage = $config['sessionStorage'] ?? null;\n        $sessionStorageId = $config['sessionStorageId'] ?? 'auth0_session';\n\n        if (false === $sessionStorage || 'cookie' === $sessionStorage) {\n            unset($config['sessionStorage']);\n\n            return $config;\n        }\n\n        if (null === $sessionStorage) {\n            $sessionStorage = app(SessionBridge::class, [\n                'prefix' => $sessionStorageId,\n            ]);\n        }\n\n        if (is_string($sessionStorage)) {\n            $sessionStorage = app(trim($sessionStorage), [\n                'prefix' => $sessionStorageId,\n            ]);\n        }\n\n        $config['sessionStorage'] = $sessionStorage instanceof StoreInterface ? $sessionStorage : null;\n\n        return $config;\n    }\n\n    protected function bootStrategy(array $config): array\n    {\n        $strategy = $config['strategy'] ?? SdkConfiguration::STRATEGY_REGULAR;\n\n        if (! is_string($strategy)) {\n            $strategy = SdkConfiguration::STRATEGY_REGULAR;\n        }\n\n        $config['strategy'] = $strategy;\n\n        return $config;\n    }\n\n    protected function bootTokenCache(array $config): array\n    {\n        $tokenCache = $config['tokenCache'] ?? null;\n\n        if (false === $tokenCache) {\n            unset($config['tokenCache']);\n\n            return $config;\n        }\n\n        if (null === $tokenCache) {\n            $tokenCache = $this->getTokenCachePool();\n        }\n\n        if (is_string($tokenCache)) {\n            $tokenCache = app(trim($tokenCache));\n        }\n\n        $config['tokenCache'] = $tokenCache instanceof CacheItemPoolInterface ? $tokenCache : null;\n\n        return $config;\n    }\n\n    protected function bootTransientStorage(array $config): array\n    {\n        $transientStorage = $config['transientStorage'] ?? null;\n        $transientStorageId = $config['transientStorageId'] ?? 'auth0_transient';\n\n        if (false === $transientStorage || 'cookie' === $transientStorage) {\n            unset($config['transientStorage']);\n\n            return $config;\n        }\n\n        if (null === $transientStorage) {\n            $transientStorage = app(SessionBridge::class, [\n                'prefix' => $transientStorageId,\n            ]);\n        }\n\n        if (is_string($transientStorage)) {\n            $transientStorage = app(trim($transientStorage), [\n                'prefix' => $transientStorageId,\n            ]);\n        }\n\n        $config['transientStorage'] = $transientStorage instanceof StoreInterface ? $transientStorage : null;\n\n        return $config;\n    }\n\n    protected function createConfiguration(\n        array $configuration,\n    ): SdkConfiguration {\n        Events::dispatch(new BuildingConfigurationEvent($configuration));\n\n        $configuration = $this->bootStrategy($configuration);\n        $configuration = $this->bootTokenCache($configuration);\n        $configuration = $this->bootManagementTokenCache($configuration);\n\n        if (in_array($configuration['strategy'], SdkConfiguration::STRATEGIES_USING_SESSIONS, true)) {\n            $configuration = $this->bootSessionStorage($configuration);\n            $configuration = $this->bootTransientStorage($configuration);\n        }\n\n        $sdkConfiguration = new SdkConfiguration($configuration);\n\n        Events::dispatch(new BuiltConfigurationEvent($sdkConfiguration));\n\n        return $sdkConfiguration;\n    }\n\n    protected function getBackchannelLogoutCachePool(): CacheItemPoolInterface\n    {\n        if (! $this->backchannelLogoutCachePool instanceof CacheItemPoolInterface) {\n            $this->backchannelLogoutCachePool = app(CacheBridge::class);\n        }\n\n        return $this->backchannelLogoutCachePool;\n    }\n\n    protected function getManagementTokenCachePool(): CacheItemPoolInterface\n    {\n        if (! $this->managementTokenCachePool instanceof CacheItemPoolInterface) {\n            $this->managementTokenCachePool = app(CacheBridge::class);\n        }\n\n        return $this->managementTokenCachePool;\n    }\n\n    protected function getTokenCachePool(): CacheItemPoolInterface\n    {\n        if (! $this->tokenCachePool instanceof CacheItemPoolInterface) {\n            $this->tokenCachePool = app(CacheBridge::class);\n        }\n\n        return $this->tokenCachePool;\n    }\n\n    /**\n     * Updates the Auth0 PHP SDK's telemetry to include the correct Laravel markers.\n     */\n    protected function setSdkTelemetry(): self\n    {\n        HttpTelemetry::setEnvProperty('Laravel', app()->version());\n        HttpTelemetry::setPackage('laravel', Service::VERSION);\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "src/Entities/InstanceEntityContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Entities;\n\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Contract\\API\\ManagementInterface;\nuse Auth0\\SDK\\Contract\\Auth0Interface;\n\n/**\n * @api\n */\ninterface InstanceEntityContract extends EntityContract\n{\n    /**\n     * Create/return instance of the Auth0-PHP SdkConfiguration.\n     */\n    public function getConfiguration(): SdkConfiguration;\n\n    /**\n     * Create/return instance of the Auth0-PHP SDK.\n     */\n    public function getSdk(): Auth0Interface;\n\n    /**\n     * Returns an instance of the Management API class.\n     */\n    public function management(): ManagementInterface;\n\n    /**\n     * Resets and cleans up the internal state of the SDK.\n     */\n    public function reset(): self;\n\n    /**\n     * Assign the Auth0-PHP SdkConfiguration.\n     *\n     * @param null|array<string>|SdkConfiguration $configuration\n     */\n    public function setConfiguration(SdkConfiguration | array | null $configuration): self;\n\n    /**\n     * Create/return instance of the Auth0-PHP SDK.\n     *\n     * @param Auth0Interface $sdk\n     */\n    public function setSdk(Auth0Interface $sdk): Auth0Interface;\n}\n"
  },
  {
    "path": "src/Entities/InstanceEntityTrait.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Entities;\n\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Contract\\Auth0Interface;\n\nuse function is_array;\n\n/**\n * @api\n */\ntrait InstanceEntityTrait\n{\n    public function reset(): self\n    {\n        unset($this->sdk, $this->configuration);\n\n        $this->sdk = null;\n        $this->configuration = null;\n\n        return $this;\n    }\n\n    /**\n     * @param null|array<string>|SdkConfiguration $configuration\n     */\n    public function setConfiguration(\n        SdkConfiguration | array | null $configuration = null,\n    ): self {\n        if (is_array($configuration)) {\n            $configuration = $this->createConfiguration($configuration);\n        }\n\n        $this->configuration = $configuration;\n\n        if ($this->configuration instanceof SdkConfiguration && $this->sdk instanceof Auth0Interface) {\n            $this->sdk->setConfiguration($this->configuration);\n        }\n\n        return $this;\n    }\n\n    public static function create(\n        SdkConfiguration | array | null $configuration = null,\n        ?string $guardConfigurationName = null,\n    ): self {\n        $instance = new self();\n\n        if (null !== $guardConfigurationName) {\n            $instance->setGuardConfigurationKey($guardConfigurationName);\n        }\n\n        if (null !== $configuration) {\n            $instance->setConfiguration($configuration);\n        }\n\n        return $instance;\n    }\n}\n"
  },
  {
    "path": "src/Events/Auth0EventContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface Auth0EventContract extends EventContract\n{\n}\n"
  },
  {
    "path": "src/Events/AuthenticationFailed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * Dispatched when user authentication fails.\n *\n * @api\n */\nfinal class AuthenticationFailed extends AuthenticationFailedAbstract implements AuthenticationFailedContract\n{\n}\n"
  },
  {
    "path": "src/Events/AuthenticationFailedAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\nuse Throwable;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class AuthenticationFailedAbstract extends EventAbstract\n{\n    /**\n     * @param Throwable $exception      Exception to be thrown following this event.\n     * @param bool      $throwException Whether or not $exception will be thrown\n     */\n    public function __construct(\n        public Throwable &$exception,\n        public bool $throwException = true,\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'exception' => json_decode(json_encode($this->exception, JSON_THROW_ON_ERROR), true),\n            'throwException' => $this->throwException,\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/AuthenticationFailedContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface AuthenticationFailedContract extends EventContract\n{\n    /**\n     * @return array{exception: array<array-key, mixed>, throwException: bool}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events/AuthenticationSucceeded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * Dispatched when a user successfully authenticates.\n *\n * @api\n */\nfinal class AuthenticationSucceeded extends AuthenticationSucceededAbstract implements AuthenticationSucceededContract\n{\n}\n"
  },
  {
    "path": "src/Events/AuthenticationSucceededAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class AuthenticationSucceededAbstract extends EventAbstract\n{\n    /**\n     * @param Authenticatable $user The user that successfully authenticated.\n     */\n    public function __construct(\n        public Authenticatable &$user,\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     *\n     * @return array{user: mixed}\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'user' => json_decode(json_encode($this->user, JSON_THROW_ON_ERROR), true),\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/AuthenticationSucceededContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface AuthenticationSucceededContract extends EventContract\n{\n    /**\n     * @return array{user: array<array-key, mixed>}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events/Configuration/BuildingConfigurationEvent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Configuration;\n\n/**\n * Dispatched immediately before the Auth0 SDK configuration object is built, allowing for modification of the configuration array.\n *\n * @api\n */\nfinal class BuildingConfigurationEvent extends BuildingConfigurationEventAbstract implements BuildingConfigurationEventContract\n{\n}\n"
  },
  {
    "path": "src/Events/Configuration/BuildingConfigurationEventAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Configuration;\n\nuse Auth0\\Laravel\\Events\\EventAbstract;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class BuildingConfigurationEventAbstract extends EventAbstract\n{\n    /**\n     * @param array<mixed> $configuration a configuration array for use with the Auth0-PHP SDK\n     */\n    public function __construct(\n        public array &$configuration,\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     *\n     * @return array{configuration: mixed}\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'configuration' => json_decode(json_encode($this->configuration, JSON_THROW_ON_ERROR), true),\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/Configuration/BuildingConfigurationEventContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Configuration;\n\nuse Auth0\\Laravel\\Events\\EventContract;\n\n/**\n * @api\n */\ninterface BuildingConfigurationEventContract extends EventContract\n{\n    /**\n     * @return array{configuration: array<array-key, mixed>}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events/Configuration/BuiltConfigurationEvent.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Configuration;\n\n/**\n * Dispatched after the Auth0 SDK configuration has been built and initialized.\n *\n * @api\n */\nfinal class BuiltConfigurationEvent extends BuiltConfigurationEventAbstract implements BuiltConfigurationEventContract\n{\n}\n"
  },
  {
    "path": "src/Events/Configuration/BuiltConfigurationEventAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Configuration;\n\nuse Auth0\\Laravel\\Events\\EventAbstract;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class BuiltConfigurationEventAbstract extends EventAbstract\n{\n    /**\n     * @param SdkConfiguration $configuration an instance of SdkConfiguration for use with the Auth0-PHP SDK\n     */\n    public function __construct(\n        public SdkConfiguration &$configuration,\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     *\n     * @return array{configuration: mixed}\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'configuration' => json_decode(json_encode($this->configuration, JSON_THROW_ON_ERROR), true),\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/Configuration/BuiltConfigurationEventContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Configuration;\n\nuse Auth0\\Laravel\\Events\\EventContract;\n\n/**\n * @api\n */\ninterface BuiltConfigurationEventContract extends EventContract\n{\n    /**\n     * @return array{configuration: array<array-key, mixed>}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events/EventAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\nuse JsonSerializable;\n\n/**\n * @psalm-suppress UnsafeInstantiation,InvalidReturnType\n *\n * @api\n */\nabstract class EventAbstract implements JsonSerializable\n{\n}\n"
  },
  {
    "path": "src/Events/EventContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface EventContract\n{\n    /**\n     * A representation of the event context which can be serialized by json_encode().\n     *\n     * @psalm-suppress LessSpecificImplementedReturnType\n     */\n    public function jsonSerialize(): mixed;\n}\n"
  },
  {
    "path": "src/Events/LoginAttempting.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * Dispatched when a user tries to login.\n *\n * @api\n */\nfinal class LoginAttempting extends LoginAttemptingAbstract implements LoginAttemptingContract\n{\n}\n"
  },
  {
    "path": "src/Events/LoginAttemptingAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class LoginAttemptingAbstract extends EventAbstract\n{\n    /**\n     * @param array<string, null|int|string> $parameters Additional API parameters to be sent with the authentication request.\n     */\n    public function __construct(\n        public array $parameters = [],\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     *\n     * @return array{parameters: mixed}\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'parameters' => json_decode(json_encode($this->parameters, JSON_THROW_ON_ERROR), true),\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/LoginAttemptingContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface LoginAttemptingContract extends EventContract\n{\n    /**\n     * @return array{parameters: array<array-key, mixed>}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events/Middleware/StatefulMiddlewareRequest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Middleware;\n\n/**\n * Dispatched when an incoming request is being processed by the stateful middleware.\n *\n * @api\n */\nfinal class StatefulMiddlewareRequest extends StatefulMiddlewareRequestAbstract implements StatefulMiddlewareRequestContract\n{\n}\n"
  },
  {
    "path": "src/Events/Middleware/StatefulMiddlewareRequestAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Middleware;\n\nuse Auth0\\Laravel\\Events\\EventAbstract;\nuse Illuminate\\Contracts\\Auth\\Guard;\nuse Illuminate\\Http\\Request;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class StatefulMiddlewareRequestAbstract extends EventAbstract\n{\n    /**\n     * @param Request $request Incoming request.\n     * @param Guard   $guard   The guard being used to authenticate the request.\n     */\n    public function __construct(\n        public Request &$request,\n        public Guard &$guard,\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     *\n     * @return array{request: mixed, guard: mixed}\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'request' => json_decode(json_encode($this->request, JSON_THROW_ON_ERROR), true),\n            'guard' => json_decode(json_encode($this->guard, JSON_THROW_ON_ERROR), true),\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/Middleware/StatefulMiddlewareRequestContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Middleware;\n\nuse Auth0\\Laravel\\Events\\EventContract;\nuse Illuminate\\Contracts\\Auth\\Guard;\nuse Illuminate\\Http\\Request;\n\n/**\n * @api\n */\ninterface StatefulMiddlewareRequestContract extends EventContract\n{\n    /**\n     * @return array{request: array<array-key, mixed>, guard: array<array-key, mixed>}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events/Middleware/StatelessMiddlewareRequest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Middleware;\n\n/**\n * Dispatched when an incoming request is being processed by the stateless middleware.\n *\n * @api\n */\nfinal class StatelessMiddlewareRequest extends StatelessMiddlewareRequestAbstract implements StatelessMiddlewareRequestContract\n{\n}\n"
  },
  {
    "path": "src/Events/Middleware/StatelessMiddlewareRequestAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Middleware;\n\nuse Auth0\\Laravel\\Events\\EventAbstract;\nuse Illuminate\\Contracts\\Auth\\Guard;\nuse Illuminate\\Http\\Request;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class StatelessMiddlewareRequestAbstract extends EventAbstract\n{\n    /**\n     * @param Request $request Incoming request.\n     * @param Guard   $guard   The guard being used to authenticate the request.\n     */\n    public function __construct(\n        public Request &$request,\n        public Guard &$guard,\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     *\n     * @return array{request: mixed, guard: mixed}\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'request' => json_decode(json_encode($this->request, JSON_THROW_ON_ERROR), true),\n            'guard' => json_decode(json_encode($this->guard, JSON_THROW_ON_ERROR), true),\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/Middleware/StatelessMiddlewareRequestContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events\\Middleware;\n\nuse Auth0\\Laravel\\Events\\EventContract;\nuse Illuminate\\Contracts\\Auth\\Guard;\nuse Illuminate\\Http\\Request;\n\n/**\n * @api\n */\ninterface StatelessMiddlewareRequestContract extends EventContract\n{\n    /**\n     * @return array{request: array<array-key, mixed>, guard: array<array-key, mixed>}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events/TokenExpired.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * Dispatched when an access token has expired.\n *\n * @api\n */\nfinal class TokenExpired extends TokenExpiredAbstract implements TokenExpiredContract\n{\n}\n"
  },
  {
    "path": "src/Events/TokenExpiredAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class TokenExpiredAbstract extends EventAbstract\n{\n    final public function jsonSerialize(): ?array\n    {\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/Events/TokenExpiredContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface TokenExpiredContract extends EventContract\n{\n    public function jsonSerialize(): ?array;\n}\n"
  },
  {
    "path": "src/Events/TokenRefreshFailed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * Dispatched when a token refresh fails.\n *\n * @api\n */\nfinal class TokenRefreshFailed extends TokenRefreshFailedAbstract implements TokenRefreshFailedContract\n{\n}\n"
  },
  {
    "path": "src/Events/TokenRefreshFailedAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class TokenRefreshFailedAbstract extends EventAbstract\n{\n    final public function jsonSerialize(): ?array\n    {\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/Events/TokenRefreshFailedContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface TokenRefreshFailedContract extends EventContract\n{\n    public function jsonSerialize(): ?array;\n}\n"
  },
  {
    "path": "src/Events/TokenRefreshSucceeded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * Dispatched when an access token is successfully refreshed.\n *\n * @api\n */\nfinal class TokenRefreshSucceeded extends TokenRefreshSucceededAbstract implements TokenRefreshSucceededContract\n{\n}\n"
  },
  {
    "path": "src/Events/TokenRefreshSucceededAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class TokenRefreshSucceededAbstract extends EventAbstract\n{\n    final public function jsonSerialize(): ?array\n    {\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/Events/TokenRefreshSucceededContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface TokenRefreshSucceededContract extends EventContract\n{\n    public function jsonSerialize(): ?array;\n}\n"
  },
  {
    "path": "src/Events/TokenVerificationAttempting.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * Dispatched before a token verification is performed.\n *\n * @api\n */\nfinal class TokenVerificationAttempting extends TokenVerificationAttemptingAbstract implements TokenVerificationAttemptingContract\n{\n}\n"
  },
  {
    "path": "src/Events/TokenVerificationAttemptingAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class TokenVerificationAttemptingAbstract extends EventAbstract\n{\n    /**\n     * @param string $token Encoded JSON Web Token that will be verification.\n     */\n    public function __construct(\n        public string $token,\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     *\n     * @return array{token: string}\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'token' => $this->token,\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/TokenVerificationAttemptingContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface TokenVerificationAttemptingContract extends EventContract\n{\n    /**\n     * @return array{token: string}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events/TokenVerificationFailed.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * Dispatched when a token fails verification.\n *\n * @api\n */\nfinal class TokenVerificationFailed extends TokenVerificationFailedAbstract implements TokenVerificationFailedContract\n{\n}\n"
  },
  {
    "path": "src/Events/TokenVerificationFailedAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\nuse Throwable;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class TokenVerificationFailedAbstract extends EventAbstract\n{\n    /**\n     * @param string    $token          Encoded JSON Web Token that failed verification.\n     * @param Throwable $exception      Exception to be thrown following this event.\n     * @param bool      $throwException Whether or not $exception will be thrown\n     */\n    public function __construct(\n        public string $token,\n        public Throwable &$exception,\n        public bool $throwException = false,\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'token' => $this->token,\n            'exception' => json_decode(json_encode($this->exception, JSON_THROW_ON_ERROR), true),\n            'throwException' => $this->throwException,\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/TokenVerificationFailedContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface TokenVerificationFailedContract extends EventContract\n{\n    /**\n     * @return array{token: string, exception: array<array-key, mixed>, throwException: bool}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events/TokenVerificationSucceeded.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * Dispatched when a token is successfully verified.\n *\n * @api\n */\nfinal class TokenVerificationSucceeded extends TokenVerificationSucceededAbstract implements TokenVerificationSucceededContract\n{\n}\n"
  },
  {
    "path": "src/Events/TokenVerificationSucceededAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class TokenVerificationSucceededAbstract extends EventAbstract\n{\n    /**\n     * @param string $token   JSON Web Token that was verified.\n     * @param array  $payload The decoded contents of the verified JSON Web Token.\n     */\n    public function __construct(\n        public string $token,\n        public array $payload,\n    ) {\n    }\n\n    /**\n     * @psalm-suppress LessSpecificImplementedReturnType\n     *\n     * @return array{token: string, payload: mixed[]}\n     */\n    final public function jsonSerialize(): array\n    {\n        return [\n            'token' => $this->token,\n            'payload' => $this->payload,\n        ];\n    }\n}\n"
  },
  {
    "path": "src/Events/TokenVerificationSucceededContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Events;\n\n/**\n * @api\n */\ninterface TokenVerificationSucceededContract extends EventContract\n{\n    /**\n     * @return array{token: string, payload: array<array-key, mixed>}\n     */\n    public function jsonSerialize(): array;\n}\n"
  },
  {
    "path": "src/Events.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Events\\{\n    AuthenticationFailed,\n    AuthenticationSucceeded,\n    EventContract,\n    LoginAttempting,\n    TokenExpired,\n    TokenRefreshFailed,\n    TokenRefreshSucceeded,\n    TokenVerificationAttempting,\n    TokenVerificationFailed,\n    TokenVerificationSucceeded,\n};\n\nuse Auth0\\Laravel\\Events\\Configuration\\{\n    BuildingConfigurationEvent,\n    BuiltConfigurationEvent,\n};\n\nuse Auth0\\Laravel\\Events\\Middleware\\{\n    StatefulMiddlewareRequest,\n    StatelessMiddlewareRequest,\n};\n\n/**\n * @api\n *\n * @codeCoverageIgnore\n */\nfinal class Events implements EventsContract\n{\n    /**\n     * @var string\n     */\n    private const TELESCOPE = '\\Laravel\\Telescope\\Telescope';\n\n    public static bool $enabled = true;\n\n    public static function dispatch(EventContract $event): void\n    {\n        if (self::$enabled) {\n            if (self::withoutTelescopeRecording(self::getName($event))) {\n                self::TELESCOPE::withoutRecording(static fn (): mixed => event(self::getName($event), $event));\n\n                return;\n            }\n\n            event(self::getName($event), $event);\n        }\n    }\n\n    public static function framework(object $event): void\n    {\n        if (self::$enabled) {\n            if (self::withoutTelescopeRecording($event::class)) {\n                self::TELESCOPE::withoutRecording(static fn (): mixed => event($event));\n\n                return;\n            }\n\n            event($event);\n        }\n    }\n\n    private static function getName(EventContract $event): string\n    {\n        return match ($event::class) {\n            AuthenticationFailed::class => self::AUTHENTICATION_FAILED,\n            AuthenticationSucceeded::class => self::AUTHENTICATION_SUCCEEDED,\n            BuildingConfigurationEvent::class => self::CONFIGURATION_BUILDING,\n            BuiltConfigurationEvent::class => self::CONFIGURATION_BUILT,\n            LoginAttempting::class => self::LOGIN_ATTEMPTING,\n            StatefulMiddlewareRequest::class => self::MIDDLEWARE_STATEFUL_REQUEST,\n            StatelessMiddlewareRequest::class => self::MIDDLEWARE_STATELESS_REQUEST,\n            TokenExpired::class => self::TOKEN_EXPIRED,\n            TokenRefreshFailed::class => self::TOKEN_REFRESH_FAILED,\n            TokenRefreshSucceeded::class => self::TOKEN_REFRESH_SUCCEEDED,\n            TokenVerificationAttempting::class => self::TOKEN_VERIFICATION_ATTEMPTING,\n            TokenVerificationFailed::class => self::TOKEN_VERIFICATION_FAILED,\n            TokenVerificationSucceeded::class => self::TOKEN_VERIFICATION_SUCCEEDED,\n            default => $event::class,\n        };\n    }\n\n    private static function withoutTelescopeRecording(string $event): bool\n    {\n        if (! class_exists(self::TELESCOPE)) {\n            return false;\n        }\n\n        return match ($event) {\n            self::CONFIGURATION_BUILDING => true,\n            self::CONFIGURATION_BUILT => true,\n            default => false,\n        };\n    }\n}\n"
  },
  {
    "path": "src/EventsContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Events\\{\n    AuthenticationFailed,\n    AuthenticationSucceeded,\n    LoginAttempting,\n    TokenExpired,\n    TokenRefreshFailed,\n    TokenRefreshSucceeded,\n    TokenVerificationAttempting,\n    TokenVerificationFailed,\n    TokenVerificationSucceeded,\n};\n\nuse Auth0\\Laravel\\Events\\Configuration\\{\n    BuildingConfigurationEvent,\n    BuiltConfigurationEvent,\n};\n\nuse Auth0\\Laravel\\Events\\EventContract;\n\nuse Auth0\\Laravel\\Events\\Middleware\\{\n    StatefulMiddlewareRequest,\n    StatelessMiddlewareRequest,\n};\n\n/**\n * @api\n */\ninterface EventsContract\n{\n    /**\n     * @var class-string<AuthenticationFailed>\n     */\n    public const AUTHENTICATION_FAILED = AuthenticationFailed::class;\n\n    /**\n     * @var class-string<AuthenticationSucceeded>\n     */\n    public const AUTHENTICATION_SUCCEEDED = AuthenticationSucceeded::class;\n\n    /**\n     * @var class-string<BuildingConfigurationEvent>\n     */\n    public const CONFIGURATION_BUILDING = BuildingConfigurationEvent::class;\n\n    /**\n     * @var class-string<BuiltConfigurationEvent>\n     */\n    public const CONFIGURATION_BUILT = BuiltConfigurationEvent::class;\n\n    /**\n     * @var class-string<LoginAttempting>\n     */\n    public const LOGIN_ATTEMPTING = LoginAttempting::class;\n\n    /**\n     * @var class-string<StatefulMiddlewareRequest>\n     */\n    public const MIDDLEWARE_STATEFUL_REQUEST = StatefulMiddlewareRequest::class;\n\n    /**\n     * @var class-string<StatelessMiddlewareRequest>\n     */\n    public const MIDDLEWARE_STATELESS_REQUEST = StatelessMiddlewareRequest::class;\n\n    /**\n     * @var class-string<TokenExpired>\n     */\n    public const TOKEN_EXPIRED = TokenExpired::class;\n\n    /**\n     * @var class-string<TokenRefreshFailed>\n     */\n    public const TOKEN_REFRESH_FAILED = TokenRefreshFailed::class;\n\n    /**\n     * @var class-string<TokenRefreshSucceeded>\n     */\n    public const TOKEN_REFRESH_SUCCEEDED = TokenRefreshSucceeded::class;\n\n    /**\n     * @var class-string<TokenVerificationAttempting>\n     */\n    public const TOKEN_VERIFICATION_ATTEMPTING = TokenVerificationAttempting::class;\n\n    /**\n     * @var class-string<TokenVerificationFailed>\n     */\n    public const TOKEN_VERIFICATION_FAILED = TokenVerificationFailed::class;\n\n    /**\n     * @var class-string<TokenVerificationSucceeded>\n     */\n    public const TOKEN_VERIFICATION_SUCCEEDED = TokenVerificationSucceeded::class;\n\n    /**\n     * Dispatch an SDK event.\n     *\n     * @param EventContract $event The event to dispatch.\n     */\n    public static function dispatch(\n        EventContract $event,\n    ): void;\n\n    /**\n     * Dispatch a Laravel framework event.\n     *\n     * @param object $event The event to dispatch.\n     */\n    public static function framework(\n        object $event,\n    ): void;\n}\n"
  },
  {
    "path": "src/Exceptions/AuthenticationException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * Exception thrown when an error occurs in the SDK's authentication flow.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\nfinal class AuthenticationException extends AuthenticationExceptionAbstract implements AuthenticationExceptionContract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/AuthenticationExceptionAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * @codeCoverageIgnore\n *\n * @internal\n *\n * @api\n */\nabstract class AuthenticationExceptionAbstract extends ExceptionAbstract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/AuthenticationExceptionContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * Exception thrown when an error occurs in the SDK's authentication flow.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\ninterface AuthenticationExceptionContract extends ExceptionContract\n{\n    /**\n     * @var string\n     */\n    public const UNAUTHENTICATED = 'Unauthenticated.';\n}\n"
  },
  {
    "path": "src/Exceptions/ControllerException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * Exception thrown when an error occurs in the SDK's controllers.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\nfinal class ControllerException extends ControllerExceptionAbstract implements ControllerExceptionContract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/ControllerExceptionAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * @codeCoverageIgnore\n *\n * @internal\n *\n * @api\n */\nabstract class ControllerExceptionAbstract extends ExceptionAbstract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/ControllerExceptionContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * Exception thrown when an error occurs in the SDK's controllers.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\ninterface ControllerExceptionContract extends ExceptionContract\n{\n    /**\n     * @var string\n     */\n    public const ROUTED_USING_INCOMPATIBLE_GUARD = 'Requests to this controller must be routed through a Guard configured with an Auth0 driver.';\n}\n"
  },
  {
    "path": "src/Exceptions/Controllers/CallbackControllerException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions\\Controllers;\n\n/**\n * Exception thrown when an error occurs in the SDK's callback handler.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\nfinal class CallbackControllerException extends CallbackControllerExceptionAbstract implements CallbackControllerExceptionContract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/Controllers/CallbackControllerExceptionAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions\\Controllers;\n\nuse Auth0\\Laravel\\Exceptions\\ExceptionAbstract;\n\n/**\n * @codeCoverageIgnore\n *\n * @internal\n *\n * @api\n */\nabstract class CallbackControllerExceptionAbstract extends ExceptionAbstract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/Controllers/CallbackControllerExceptionContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions\\Controllers;\n\nuse Auth0\\Laravel\\Exceptions\\ExceptionContract;\n\n/**\n * Exception thrown when an error occurs in the SDK's callback handler.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\ninterface CallbackControllerExceptionContract extends ExceptionContract\n{\n    /**\n     * @var string\n     */\n    public const MSG_API_RESPONSE = '%s: %s';\n}\n"
  },
  {
    "path": "src/Exceptions/ExceptionAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\nuse Exception;\n\n/**\n * Base exception for all Auth0 Laravel SDK exceptions.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\nabstract class ExceptionAbstract extends Exception\n{\n}\n"
  },
  {
    "path": "src/Exceptions/ExceptionContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\nuse Auth0\\SDK\\Exception\\Auth0Exception;\n\n/**\n * Base exception for all Auth0 Laravel SDK exceptions.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\ninterface ExceptionContract extends Auth0Exception\n{\n}\n"
  },
  {
    "path": "src/Exceptions/GuardException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * Exception thrown when an error occurs in the SDK's guards.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\nfinal class GuardException extends GuardExceptionAbstract implements GuardExceptionContract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/GuardExceptionAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * @codeCoverageIgnore\n *\n * @internal\n *\n * @api\n */\nabstract class GuardExceptionAbstract extends ExceptionAbstract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/GuardExceptionContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * Exception thrown when an error occurs in the SDK's guards.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\ninterface GuardExceptionContract extends ExceptionContract\n{\n    /**\n     * @var string\n     */\n    public const USER_MODEL_NORMALIZATION_FAILURE = 'Unable to convert user to array. Class should implement JsonSerializable, Arrayable or Jsonable.';\n\n    /**\n     * @var string\n     */\n    public const USER_PROVIDER_UNAVAILABLE = 'Unable to create User Provider %s from configuration.';\n\n    /**\n     * @var string\n     */\n    public const USER_PROVIDER_UNCONFIGURED = 'There is no User Provider configured. Please ensure the `provider` key is set in the Guard configuration, and points to a valid entry in the `providers` configuration.';\n}\n"
  },
  {
    "path": "src/Exceptions/SessionException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * Exception thrown when an error occurs with the Laravel session store.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\nfinal class SessionException extends SessionExceptionAbstract implements SessionExceptionContract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/SessionExceptionAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * @codeCoverageIgnore\n *\n * @internal\n *\n * @api\n */\nabstract class SessionExceptionAbstract extends ExceptionAbstract\n{\n}\n"
  },
  {
    "path": "src/Exceptions/SessionExceptionContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Exceptions;\n\n/**\n * Exception thrown when an error occurs with the Laravel session store.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\ninterface SessionExceptionContract extends ExceptionContract\n{\n    /**\n     * @var string\n     */\n    public const LARAVEL_SESSION_INACCESSIBLE = 'The Laravel session store is inaccessible.';\n}\n"
  },
  {
    "path": "src/Facade/Auth0.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Facade;\n\nuse Illuminate\\Support\\Facades\\Facade;\n\n/**\n * Facade for the Auth0 SDK.\n *\n * @codeCoverageIgnore\n *\n * @api\n */\nfinal class Auth0 extends Facade\n{\n    /**\n     * @return string\n     */\n    protected static function getFacadeAccessor()\n    {\n        return 'auth0';\n    }\n}\n"
  },
  {
    "path": "src/Guards/AuthenticationGuard.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Guards;\n\nuse Auth0\\Laravel\\Entities\\{CredentialEntity, CredentialEntityContract};\nuse Auth0\\Laravel\\Events;\nuse Auth0\\Laravel\\Events\\{TokenRefreshFailed, TokenRefreshSucceeded};\nuse Auth0\\Laravel\\Users\\StatefulUser;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token\\Parser;\nuse Auth0\\SDK\\Utility\\HttpResponse;\nuse Illuminate\\Auth\\Events\\{Login, Logout};\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Support\\Traits\\Macroable;\n\nuse Throwable;\n\nuse function count;\nuse function is_array;\nuse function is_object;\n\n/**\n * Authentication guard for stateful sessions.\n *\n * @api\n */\nfinal class AuthenticationGuard extends GuardAbstract implements AuthenticationGuardContract\n{\n    use Macroable;\n\n    /**\n     * @var string\n     */\n    protected const TELESCOPE = '\\Laravel\\Telescope\\Telescope';\n\n    private ?string $credThumbprint = null;\n\n    public function find(): ?CredentialEntityContract\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter();\n        }\n\n        return $this->findSession();\n    }\n\n    public function findSession(): ?CredentialEntityContract\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter();\n        }\n\n        $this->getSession();\n        $session = $this->pullState();\n        $user = $session?->getUser();\n\n        if ($session instanceof CredentialEntityContract && $user instanceof Authenticatable) {\n            $user = $this->getProvider()->retrieveByCredentials($this->normalizeUserArray($user));\n\n            if ($user instanceof Authenticatable) {\n                $scope = $session->getAccessTokenScope();\n                $decoded = $session->getAccessTokenDecoded();\n\n                /**\n                 * @var array<string> $scope\n                 * @var array<string> $decoded\n                 */\n                $credential = CredentialEntity::create(\n                    user: $user,\n                    idToken: $session->getIdToken(),\n                    accessToken: $session->getAccessToken(),\n                    accessTokenDecoded: $decoded,\n                    accessTokenScope: $scope,\n                    accessTokenExpiration: $session->getAccessTokenExpiration(),\n                    refreshToken: $session->getRefreshToken(),\n                );\n\n                return $this->refreshSession($credential);\n            }\n        }\n\n        return null;\n    }\n\n    public function forgetUser(): self\n    {\n        $this->setCredential();\n\n        return $this;\n    }\n\n    public function getCredential(): ?CredentialEntityContract\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter();\n        }\n\n        if ($this->credential instanceof CredentialEntityContract) {\n            $currThumbprint = $this->getCredThumbprint($this->sdk()->getCredentials());\n            if ($currThumbprint !== $this->credThumbprint) {\n                $updated = $this->findSession();\n                $this->setCredential($updated);\n                $this->pushState($updated);\n                $this->credThumbprint = $currThumbprint;\n            }\n        }\n\n        return $this->credential;\n    }\n\n    public function hashPasswordForCookie(string $passwordHash): string\n    {\n        /** @var string $key */\n        $key = config('app.key') ?? 'base-key-for-password-hash-mac';\n\n        return hash_hmac(\n            'sha256',\n            $passwordHash,\n            $key,\n        );\n    }\n\n    public function login(\n        ?CredentialEntityContract $credential,\n    ): self {\n        $this->stopImpersonating();\n\n        $this->setCredential($credential);\n        $this->pushState($credential);\n\n        if ($credential instanceof CredentialEntityContract) {\n            $user = $credential->getUser();\n\n            if ($user instanceof Authenticatable) {\n                Events::framework(new Login(self::class, $user, true));\n            }\n        }\n\n        return $this;\n    }\n\n    public function logout(): self\n    {\n        $user = $this->user();\n\n        if ($user instanceof Authenticatable) {\n            Events::framework(new Logout(self::class, $user));\n        }\n\n        $this->stopImpersonating();\n        $this->setCredential();\n        $this->pushState();\n        $this->forgetUser();\n\n        return $this;\n    }\n\n    public function pushState(\n        ?CredentialEntityContract $credential = null,\n    ): self {\n        $sdk = $this->sdk();\n        $credential ??= $this->getCredential();\n\n        if (! $credential instanceof CredentialEntityContract) {\n            $sdk->clear(true);\n\n            return $this;\n        }\n\n        $user = $credential->getUser();\n        $idToken = $credential->getIdToken();\n        $accessToken = $credential->getAccessToken();\n        $accessTokenScope = $credential->getAccessTokenScope();\n        $accessTokenExpiration = $credential->getAccessTokenExpiration();\n        $refreshToken = $credential->getRefreshToken();\n\n        if ($user instanceof Authenticatable) {\n            $update = $this->normalizeUserArray($user);\n            $current = $sdk->getUser() ?? [];\n\n            if (count($update) !== count($current) || [] !== array_diff(array_map('serialize', $update), array_map('serialize', $current))) {\n                $sdk->setUser($update);\n            }\n        }\n\n        if (null !== $idToken && $idToken !== $sdk->getIdToken()) {\n            $sdk->setIdToken($idToken);\n        }\n\n        if (null !== $accessToken && $accessToken !== $sdk->getAccessToken()) {\n            $sdk->setAccessToken($accessToken);\n        }\n\n        if (null !== $accessTokenScope && $accessTokenScope !== $sdk->getAccessTokenScope()) {\n            /**\n             * @var array<string> $accessTokenScope\n             */\n            $sdk->setAccessTokenScope($accessTokenScope);\n        }\n\n        if (null !== $accessTokenExpiration && $accessTokenExpiration !== $sdk->getAccessTokenExpiration()) {\n            $sdk->setAccessTokenExpiration($accessTokenExpiration);\n        }\n\n        if (null !== $refreshToken && $refreshToken !== $sdk->getRefreshToken()) {\n            $sdk->setRefreshToken($refreshToken);\n        }\n\n        return $this;\n    }\n\n    public function refreshUser(): void\n    {\n        if ($this->isImpersonating()) {\n            return;\n        }\n\n        if ($this->check()) {\n            $credential = $this->getCredential();\n            $accessToken = $credential?->getAccessToken();\n\n            if (! $credential instanceof CredentialEntityContract || null === $accessToken) {\n                return;\n            }\n\n            $response = $this->sdk()->authentication()->userInfo($accessToken);\n\n            if (HttpResponse::wasSuccessful($response)) {\n                $response = HttpResponse::decodeContent($response);\n\n                if (! is_array($response)) {\n                    return;\n                }\n\n                $user = $this->getProvider()->retrieveByCredentials($response);\n                $scope = $credential->getAccessTokenScope();\n                $decoded = $credential->getAccessTokenDecoded();\n\n                /**\n                 * @var array<string> $scope\n                 * @var array<string> $decoded\n                 */\n                $this->pushState(CredentialEntity::create(\n                    user: $user,\n                    idToken: $credential->getIdToken(),\n                    accessToken: $credential->getAccessToken(),\n                    accessTokenScope: $scope,\n                    accessTokenDecoded: $decoded,\n                    accessTokenExpiration: $credential->getAccessTokenExpiration(),\n                    refreshToken: $credential->getRefreshToken(),\n                ));\n            }\n        }\n    }\n\n    public function setCredential(\n        ?CredentialEntityContract $credential = null,\n    ): self {\n        $this->stopImpersonating();\n\n        $this->credential = $credential;\n\n        return $this;\n    }\n\n    /**\n     * @param CredentialEntityContract $credential\n     */\n    public function setImpersonating(\n        CredentialEntityContract $credential,\n    ): self {\n        $this->impersonationSource = self::SOURCE_SESSION;\n        $this->impersonating = $credential;\n\n        return $this;\n    }\n\n    public function setUser(\n        Authenticatable $user,\n    ): self {\n        if ($this->isImpersonating()) {\n            if ($this->getImposter()?->getUser() === $user) {\n                return $this;\n            }\n\n            $this->stopImpersonating();\n        }\n\n        $credential = $this->getCredential() ?? CredentialEntity::create();\n        $credential->setUser($user);\n\n        $this->setCredential($credential);\n        $this->pushState($credential);\n\n        return $this;\n    }\n\n    public function user(): ?Authenticatable\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter()?->getUser();\n        }\n\n        static $lastResponse = null;\n\n        /**\n         * @var ?Authenticatable $lastResponse\n         */\n        // @codeCoverageIgnoreStart\n        if (class_exists(self::TELESCOPE) && true === config('telescope.enabled')) {\n            static $depth = 0;\n            static $lastCalled = null;\n\n            /**\n             * @var int  $depth\n             * @var ?int $lastCalled\n             */\n            if (null === $lastCalled) {\n                $lastCalled = time();\n            }\n\n            if (time() - $lastCalled > 10) {\n                $lastResponse = null;\n                $depth = 0;\n            }\n\n            if ($depth >= 1) {\n                return $lastResponse;\n            }\n\n            ++$depth;\n            $lastCalled = time();\n        }\n        // @codeCoverageIgnoreEnd\n\n        $currentUser = $this->getCredential()?->getUser();\n\n        if ($currentUser instanceof Authenticatable) {\n            return $lastResponse = $currentUser;\n        }\n\n        $session = $this->find();\n\n        if ($session instanceof CredentialEntityContract) {\n            $this->login($session);\n\n            return $lastResponse = $this->getCredential()?->getUser();\n        }\n\n        return $lastResponse = null;\n    }\n\n    private function getCredThumbprint(?object $credential): null | string\n    {\n        if (null === $credential) {\n            return null;\n        }\n\n        return md5(serialize($credential));\n    }\n\n    private function pullState(): ?CredentialEntityContract\n    {\n        $sdk = $this->sdk();\n        $sdk->refreshState();\n\n        $credentials = $sdk->getCredentials();\n\n        /** @var mixed $credentials */\n        if (is_object($credentials) && property_exists($credentials, 'user') && property_exists($credentials, 'idToken') && property_exists($credentials, 'accessToken') && property_exists($credentials, 'accessTokenScope') && property_exists($credentials, 'accessTokenExpiration') && property_exists($credentials, 'refreshToken')) {\n            $decoded = null;\n\n            if (null !== $credentials->accessToken) {\n                $decoded = (new Parser(new SdkConfiguration(strategy: SdkConfiguration::STRATEGY_NONE), $credentials->accessToken))->export();\n            }\n\n            /**\n             * @var null|array<string> $decoded\n             */\n\n            return CredentialEntity::create(\n                user: new StatefulUser($credentials->user),\n                idToken: $credentials->idToken,\n                accessToken: $credentials->accessToken,\n                accessTokenDecoded: $decoded,\n                accessTokenScope: $credentials->accessTokenScope,\n                accessTokenExpiration: $credentials->accessTokenExpiration,\n                refreshToken: $credentials->refreshToken,\n            );\n        }\n\n        return null;\n    }\n\n    private function refreshSession(\n        ?CredentialEntityContract $credential,\n    ): ?CredentialEntityContract {\n        if (! $credential instanceof CredentialEntityContract || true !== $credential->getAccessTokenExpired()) {\n            return $credential;\n        }\n\n        if (null === $credential->getRefreshToken()) {\n            return null;\n        }\n\n        try {\n            $this->sdk()->renew();\n            $session = $this->pullState();\n        } catch (Throwable) {\n            Events::dispatch(new TokenRefreshFailed());\n            $session = null;\n        }\n\n        if ($session instanceof CredentialEntityContract) {\n            Events::dispatch(new TokenRefreshSucceeded());\n            $user = $session->getUser();\n\n            // @codeCoverageIgnoreStart\n            if (! $user instanceof Authenticatable) {\n                return null;\n            }\n            // @codeCoverageIgnoreEnd\n\n            $user = $this->getProvider()->retrieveByCredentials($this->normalizeUserArray($user));\n\n            if ($user instanceof Authenticatable) {\n                $decoded = null;\n                $accessToken = $session->getAccessToken();\n\n                if (null !== $accessToken) {\n                    $decoded = (new Parser(new SdkConfiguration(strategy: SdkConfiguration::STRATEGY_NONE), $accessToken))->export();\n                }\n\n                $scope = $session->getAccessTokenScope();\n\n                /**\n                 * @var array<string>      $scope\n                 * @var null|array<string> $decoded\n                 */\n\n                return CredentialEntity::create(\n                    user: $user,\n                    idToken: $session->getIdToken(),\n                    accessToken: $session->getAccessToken(),\n                    accessTokenDecoded: $decoded,\n                    accessTokenScope: $scope,\n                    accessTokenExpiration: $session->getAccessTokenExpiration(),\n                    refreshToken: $session->getRefreshToken(),\n                );\n            }\n        }\n\n        $this->setCredential(null);\n        $this->pushState();\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/Guards/AuthenticationGuardContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Guards;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\n\n/**\n * @api\n */\ninterface AuthenticationGuardContract extends GuardContract\n{\n    /**\n     * Searches for an available credential from a specified source in the current request context.\n     */\n    public function find(): ?CredentialEntityContract;\n\n    /**\n     * Get a credential candidate from an Auth0-PHP SDK session.\n     *\n     * @return null|CredentialEntityContract Credential when a valid token is found, null otherwise.\n     */\n    public function findSession(): ?CredentialEntityContract;\n\n    public function login(\n        ?CredentialEntityContract $credential,\n    ): self;\n\n    public function pushState(\n        ?CredentialEntityContract $credential = null,\n    ): self;\n\n    public function setCredential(\n        ?CredentialEntityContract $credential = null,\n    ): self;\n}\n"
  },
  {
    "path": "src/Guards/AuthorizationGuard.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Guards;\n\nuse Auth0\\Laravel\\Entities\\{CredentialEntity, CredentialEntityContract};\nuse Auth0\\Laravel\\UserProviderContract;\nuse Auth0\\SDK\\Utility\\HttpResponse;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Support\\Traits\\Macroable;\n\nuse function is_array;\nuse function is_string;\n\n/**\n * Authorization guard for stateless token-based authentication.\n *\n * @api\n */\nfinal class AuthorizationGuard extends GuardAbstract implements AuthorizationGuardContract\n{\n    use Macroable;\n\n    public function find(): ?CredentialEntityContract\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter();\n        }\n\n        return $this->findToken();\n    }\n\n    public function findToken(): ?CredentialEntityContract\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter();\n        }\n\n        /**\n         * @var \\Illuminate\\Http\\Request $request\n         */\n        $request = app('request');\n\n        $token = trim($request->bearerToken() ?? '');\n\n        if ('' === $token) {\n            return null;\n        }\n\n        $decoded = $this->processToken(\n            token: $token,\n        );\n\n        /**\n         * @var null|array<string> $decoded\n         */\n        if (null === $decoded) {\n            return null;\n        }\n\n        $provider = $this->getProvider();\n\n        // @codeCoverageIgnoreStart\n        if (! $provider instanceof UserProviderContract) {\n            return null;\n        }\n        // @codeCoverageIgnoreEnd\n\n        $user = $provider->getRepository()->fromAccessToken(\n            user: $decoded,\n        );\n\n        // @codeCoverageIgnoreStart\n        if (! $user instanceof Authenticatable) {\n            return null;\n        }\n        // @codeCoverageIgnoreEnd\n\n        $data = $this->normalizeUserArray($user);\n\n        // @codeCoverageIgnoreStart\n        if ([] === $data) {\n            return null;\n        }\n        // @codeCoverageIgnoreEnd\n\n        $scope = isset($data['scope']) && is_string($data['scope']) ? explode(' ', $data['scope']) : [];\n        $exp = isset($data['exp']) && is_numeric($data['exp']) ? (int) $data['exp'] : null;\n\n        return CredentialEntity::create(\n            user: $user,\n            accessToken: $token,\n            accessTokenScope: $scope,\n            accessTokenExpiration: $exp,\n            accessTokenDecoded: $decoded,\n        );\n    }\n\n    public function forgetUser(): self\n    {\n        $this->setCredential();\n\n        return $this;\n    }\n\n    public function getCredential(): ?CredentialEntityContract\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter();\n        }\n\n        return $this->credential;\n    }\n\n    public function login(\n        ?CredentialEntityContract $credential,\n    ): self {\n        $this->stopImpersonating();\n\n        $this->setCredential($credential);\n\n        return $this;\n    }\n\n    public function logout(): self\n    {\n        $this->stopImpersonating();\n\n        $this->setCredential();\n        $this->forgetUser();\n\n        return $this;\n    }\n\n    public function refreshUser(): void\n    {\n        if ($this->isImpersonating()) {\n            return;\n        }\n\n        if ($this->check()) {\n            $credential = $this->getCredential();\n            $accessToken = $credential?->getAccessToken();\n\n            if (! $credential instanceof CredentialEntityContract || null === $accessToken) {\n                return;\n            }\n\n            $response = $this->sdk()->authentication()->userInfo($accessToken);\n\n            if (HttpResponse::wasSuccessful($response)) {\n                $response = HttpResponse::decodeContent($response);\n\n                if (! is_array($response)) {\n                    return;\n                }\n\n                $user = $this->getProvider()->retrieveByCredentials($response);\n                $scope = $credential->getAccessTokenScope();\n\n                /**\n                 * @var array<string> $scope\n                 */\n                $this->setCredential(CredentialEntity::create(\n                    user: $user,\n                    idToken: $credential->getIdToken(),\n                    accessToken: $credential->getAccessToken(),\n                    accessTokenScope: $scope,\n                    accessTokenExpiration: $credential->getAccessTokenExpiration(),\n                    refreshToken: $credential->getRefreshToken(),\n                ));\n            }\n        }\n    }\n\n    public function setCredential(\n        ?CredentialEntityContract $credential = null,\n    ): self {\n        $this->stopImpersonating();\n\n        $this->credential = $credential;\n\n        return $this;\n    }\n\n    /**\n     * @param CredentialEntityContract $credential\n     */\n    public function setImpersonating(\n        CredentialEntityContract $credential,\n    ): self {\n        $this->impersonationSource = self::SOURCE_TOKEN;\n        $this->impersonating = $credential;\n\n        return $this;\n    }\n\n    public function setUser(\n        Authenticatable $user,\n    ): self {\n        if ($this->isImpersonating()) {\n            if ($this->getImposter()?->getUser() === $user) {\n                return $this;\n            }\n\n            $this->stopImpersonating();\n        }\n\n        $credential = $this->getCredential() ?? CredentialEntity::create();\n        $credential->setUser($user);\n\n        $this->setCredential($credential);\n\n        return $this;\n    }\n\n    public function user(): ?Authenticatable\n    {\n        if ($this->isImpersonating()) {\n            return $this->getImposter()?->getUser();\n        }\n\n        $currentUser = $this->getCredential()?->getUser();\n\n        if ($currentUser instanceof Authenticatable) {\n            return $currentUser;\n        }\n\n        $token = $this->find();\n\n        if ($token instanceof CredentialEntityContract) {\n            $this->login($token);\n\n            return $this->getCredential()?->getUser();\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/Guards/AuthorizationGuardContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Guards;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\n\n/**\n * @api\n */\ninterface AuthorizationGuardContract extends GuardContract\n{\n    /**\n     * Searches for an available credential from a specified source in the current request context.\n     */\n    public function find(): ?CredentialEntityContract;\n\n    /**\n     * Get a credential candidate from a provided access token.\n     *\n     * @return null|CredentialEntityContract Credential object if a valid token is found, null otherwise.\n     */\n    public function findToken(): ?CredentialEntityContract;\n\n    public function login(\n        ?CredentialEntityContract $credential,\n    ): self;\n\n    public function setCredential(\n        ?CredentialEntityContract $credential = null,\n    ): self;\n}\n"
  },
  {
    "path": "src/Guards/GuardAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Guards;\n\nuse Auth0\\Laravel\\Entities\\{CredentialEntityContract, InstanceEntity, InstanceEntityContract};\nuse Auth0\\Laravel\\Events;\nuse Auth0\\Laravel\\Events\\{TokenVerificationAttempting, TokenVerificationFailed, TokenVerificationSucceeded};\nuse Auth0\\Laravel\\Exceptions\\{AuthenticationException, GuardException, GuardExceptionContract};\nuse Auth0\\SDK\\Contract\\API\\ManagementInterface;\nuse Auth0\\SDK\\Contract\\Auth0Interface;\nuse Auth0\\SDK\\Exception\\InvalidTokenException;\nuse Auth0\\SDK\\Token;\nuse Exception;\nuse Illuminate\\Contracts\\Auth\\{Authenticatable, Guard, UserProvider};\nuse Illuminate\\Contracts\\Session\\Session;\nuse Illuminate\\Contracts\\Support\\{Arrayable, Jsonable};\nuse JsonSerializable;\n\nuse function in_array;\nuse function is_array;\nuse function is_int;\nuse function is_string;\nuse function sprintf;\n\n/**\n * @internal\n *\n * @api\n */\nabstract class GuardAbstract implements Guard\n{\n    protected ?CredentialEntityContract $credential = null;\n\n    protected ?CredentialEntityContract $impersonating = null;\n\n    protected ?int $impersonationSource = null;\n\n    protected ?UserProvider $provider = null;\n\n    protected ?Session $session = null;\n\n    public function __construct(\n        public string $name = '',\n        protected ?array $config = null,\n        protected ?InstanceEntityContract $sdk = null,\n    ) {\n    }\n\n    final public function authenticate(): Authenticatable\n    {\n        if (($user = $this->user()) instanceof Authenticatable) {\n            return $user;\n        }\n\n        throw new AuthenticationException(AuthenticationException::UNAUTHENTICATED);\n    }\n\n    final public function check(): bool\n    {\n        return $this->hasUser();\n    }\n\n    final public function getImposter(): ?CredentialEntityContract\n    {\n        return $this->impersonating;\n    }\n\n    final public function getImposterSource(): ?int\n    {\n        return $this->impersonationSource;\n    }\n\n    final public function getName(): string\n    {\n        return $this->name;\n    }\n\n    final public function getProvider(): UserProvider\n    {\n        if ($this->provider instanceof UserProvider) {\n            return $this->provider;\n        }\n\n        $providerName = $this->config['provider'] ?? '';\n\n        if (! is_string($providerName) || '' === $providerName) {\n            // @codeCoverageIgnoreStart\n            throw new GuardException(GuardExceptionContract::USER_PROVIDER_UNCONFIGURED);\n            // @codeCoverageIgnoreEnd\n        }\n\n        $providerName = trim($providerName);\n\n        /**\n         * @var \\Illuminate\\Auth\\AuthManager $auth\n         */\n        $auth = app('auth');\n        $provider = $auth->createUserProvider($providerName);\n\n        if ($provider instanceof UserProvider) {\n            $this->provider = $provider;\n\n            return $provider;\n        }\n\n        // @codeCoverageIgnoreStart\n        throw new GuardException(sprintf(GuardExceptionContract::USER_PROVIDER_UNAVAILABLE, $providerName));\n        // @codeCoverageIgnoreEnd\n    }\n\n    final public function getRefreshedUser(): ?Authenticatable\n    {\n        $this->refreshUser();\n\n        return $this->user();\n    }\n\n    final public function getSession(): Session\n    {\n        if (! $this->session instanceof Session) {\n            /**\n             * @var \\Illuminate\\Session\\Store $store\n             */\n            $store = app('session.store');\n            /**\n             * @var \\Illuminate\\Http\\Request $request\n             */\n            $request = app('request');\n\n            if (! $request->hasSession(true)) {\n                $request->setLaravelSession($store);\n            }\n\n            if (! $store->isStarted()) {\n                $store->start();\n            }\n\n            $this->session = $store;\n        }\n\n        return $this->session;\n    }\n\n    final public function guest(): bool\n    {\n        return ! $this->check();\n    }\n\n    final public function hasPermission(\n        string $permission,\n        ?CredentialEntityContract $credential = null,\n    ): bool {\n        $permission = trim($permission);\n\n        if ('*' === $permission) {\n            return true;\n        }\n\n        $available = $credential?->getAccessTokenDecoded() ?? $this->getCredential()?->getAccessTokenDecoded() ?? [];\n        $available = $available['permissions'] ?? [];\n\n        /**\n         * @var mixed $available\n         */\n        if (! is_array($available) || [] === $available) {\n            return false;\n        }\n\n        return in_array($permission, $available, true);\n    }\n\n    final public function hasScope(\n        string $scope,\n        ?CredentialEntityContract $credential = null,\n    ): bool {\n        $scope = trim($scope);\n\n        if ('*' === $scope) {\n            return true;\n        }\n\n        $available = $credential?->getAccessTokenScope() ?? $this->getCredential()?->getAccessTokenScope() ?? [];\n\n        if ([] !== $available) {\n            return in_array($scope, $available, true);\n        }\n\n        return false;\n    }\n\n    final public function hasUser(): bool\n    {\n        return $this->user() instanceof Authenticatable;\n    }\n\n    final public function id(): string | null\n    {\n        $user = $this->user()?->getAuthIdentifier();\n\n        if (is_string($user) || is_int($user)) {\n            return (string) $user;\n        }\n\n        return null;\n    }\n\n    final public function isImpersonating(): bool\n    {\n        return $this->impersonating instanceof CredentialEntityContract;\n    }\n\n    final public function management(): ManagementInterface\n    {\n        return $this->sdk()->management();\n    }\n\n    final public function processToken(\n        string $token,\n    ): ?array {\n        Events::dispatch($event = new TokenVerificationAttempting($token));\n        $token = $event->token;\n        $decoded = null;\n\n        try {\n            $decoded = $this->sdk()->decode(token: $token, tokenType: Token::TYPE_ACCESS_TOKEN)->toArray();\n        } catch (InvalidTokenException $invalidTokenException) {\n            Events::dispatch($event = new TokenVerificationFailed($token, $invalidTokenException));\n\n            if ($event->throwException) {\n                // @codeCoverageIgnoreStart\n                throw $invalidTokenException;\n                // @codeCoverageIgnoreEnd\n            }\n\n            return null;\n        }\n\n        Events::dispatch(new TokenVerificationSucceeded($token, $decoded));\n\n        return $decoded;\n    }\n\n    final public function sdk(\n        bool $reset = false,\n    ): Auth0Interface {\n        if (! $this->sdk instanceof InstanceEntityContract || $reset) {\n            $configurationName = $this->config['configuration'] ?? $this->name;\n\n            $this->sdk = InstanceEntity::create(\n                guardConfigurationName: $configurationName,\n            );\n        }\n\n        return $this->sdk->getSdk();\n    }\n\n    /**\n     * @codeCoverageIgnore\n     */\n    final public function service(): ?InstanceEntityContract\n    {\n        return $this->sdk;\n    }\n\n    final public function stopImpersonating(): void\n    {\n        $this->impersonating = null;\n        $this->impersonationSource = null;\n    }\n\n    /**\n     * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter\n     *\n     * @param array $credentials\n     */\n    final public function validate(\n        array $credentials = [],\n    ): bool {\n        return false;\n    }\n\n    final public function viaRemember(): bool\n    {\n        return false;\n    }\n\n    abstract public function find(): ?CredentialEntityContract;\n\n    abstract public function forgetUser(): self;\n\n    abstract public function getCredential(): ?CredentialEntityContract;\n\n    abstract public function login(?CredentialEntityContract $credential): GuardContract;\n\n    abstract public function logout(): GuardContract;\n\n    abstract public function refreshUser(): void;\n\n    abstract public function setCredential(?CredentialEntityContract $credential = null): GuardContract;\n\n    /**\n     * Toggle the Guard's impersonation state. This should only be used by the Impersonate trait, and is not intended for use by end-users. It is public to allow for testing.\n     *\n     * @param CredentialEntityContract $credential\n     */\n    abstract public function setImpersonating(\n        CredentialEntityContract $credential,\n    ): self;\n\n    abstract public function setUser(\n        Authenticatable $user,\n    ): self;\n\n    abstract public function user(): ?Authenticatable;\n\n    /**\n     * Normalize a user model object for easier storage or comparison.\n     *\n     * @param Authenticatable $user User model object.\n     *\n     * @throws Exception If the user model object cannot be normalized.\n     *\n     * @return array<array|int|string> Normalized user model object.\n     *\n     * @psalm-suppress TypeDoesNotContainType, UndefinedDocblockClass, UndefinedInterfaceMethod\n     *\n     * @codeCoverageIgnore\n     */\n    protected function normalizeUserArray(\n        Authenticatable $user,\n    ): array {\n        $response = null;\n        $implements = class_implements($user, true);\n        $implements = is_array($implements) ? $implements : [];\n\n        if (isset($implements[JsonSerializable::class])) {\n            /**\n             * @var JsonSerializable $user\n             */\n            $response = json_encode($user->jsonSerialize(), JSON_THROW_ON_ERROR);\n        }\n\n        if (null === $response && isset($implements[Jsonable::class])) {\n            /**\n             * @var Jsonable $user\n             */\n            $response = $user->toJson();\n        }\n\n        if (null === $response && isset($implements[Arrayable::class])) {\n            /**\n             * @var Arrayable $user\n             */\n            try {\n                $response = json_encode($user->toArray(), JSON_THROW_ON_ERROR);\n            } catch (Exception) {\n            }\n        }\n\n        // if (null === $response && (new ReflectionClass($user))->hasMethod('attributesToArray')) {\n        //     try {\n        //         // @phpstan-ignore-next-line\n        //         $response = json_encode($user->attributesToArray(), JSON_THROW_ON_ERROR);\n        //     } catch (\\Exception) {\n        //     }\n        // }\n\n        if (is_string($response)) {\n            try {\n                $response = json_decode($response, true, 512, JSON_THROW_ON_ERROR);\n\n                if (is_array($response) && [] !== $response) {\n                    /**\n                     * @var array<array|int|string> $response\n                     */\n                    return $response;\n                }\n            } catch (Exception) {\n            }\n        }\n\n        throw new GuardException(GuardExceptionContract::USER_MODEL_NORMALIZATION_FAILURE);\n    }\n}\n"
  },
  {
    "path": "src/Guards/GuardContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Guards;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\SDK\\Contract\\API\\ManagementInterface;\nuse Auth0\\SDK\\Contract\\Auth0Interface;\nuse Illuminate\\Auth\\AuthenticationException;\nuse Illuminate\\Contracts\\Auth\\{Authenticatable, UserProvider};\nuse Illuminate\\Contracts\\Container\\BindingResolutionException;\nuse Illuminate\\Contracts\\Session\\Session;\nuse Psr\\Container\\{ContainerExceptionInterface, NotFoundExceptionInterface};\n\n/**\n * @api\n */\ninterface GuardContract\n{\n    /**\n     * @var int Credential source is a stateful session.\n     */\n    public const SOURCE_SESSION = 2;\n\n    /**\n     * @var int Credential source is a stateless token.\n     */\n    public const SOURCE_TOKEN = 1;\n\n    /**\n     * Returns the currently authenticated user for the guard. If none is set, throws an exception.\n     *\n     * @throws AuthenticationException If no user is currently authenticated.\n     */\n    public function authenticate(): Authenticatable;\n\n    /**\n     * Returns whether there is a currently authenticated user for the guard.\n     */\n    public function check(): bool;\n\n    /**\n     * Clears the currently authenticated user for the guard. This will not clear a session, if one is set.\n     */\n    public function forgetUser(): self;\n\n    /**\n     * Returns the Guard's currently configured Credential, or null if no Credential is configured.\n     */\n    public function getCredential(): ?CredentialEntityContract;\n\n    /**\n     * Get the currently impersonated user, if any.\n     */\n    public function getImposter(): ?CredentialEntityContract;\n\n    /**\n     * Returns the source context from which the guard is currently impersonating another user.\n     */\n    public function getImposterSource(): ?int;\n\n    /**\n     * Returns the Guard's currently configured UserProvider.\n     */\n    public function getProvider(): UserProvider;\n\n    /**\n     * Queries the /userinfo endpoint, updates the currently authenticated user for the guard, and returns a new Authenticatable user representing the updated user.\n     */\n    public function getRefreshedUser(): ?Authenticatable;\n\n    /**\n     * Returns a Laravel session store from the current Request context. Note that this will start a session if one is not already started.\n     */\n    public function getSession(): Session;\n\n    /**\n     * Returns whether there is no currently authenticated user for the guard.\n     */\n    public function guest(): bool;\n\n    /**\n     * Returns whether a credential has a specified permission, such as \"read:users\". Note that RBAC must be enabled for this to work.\n     *\n     * @param string                        $permission The permission to check for.\n     * @param null|CredentialEntityContract $credential Optional. The Credential to check. If omitted, the currently authenticated Credential will be used.\n     */\n    public function hasPermission(\n        string $permission,\n        ?CredentialEntityContract $credential = null,\n    ): bool;\n\n    /**\n     * Returns whether a credential has a specified scope, such as \"read:users\". Note that RBAC must be enabled for this to work.\n     *\n     * @param string                        $scope      The scope to check for.\n     * @param null|CredentialEntityContract $credential Optional. The Credential to check. If omitted, the currently authenticated Credential will be used.\n     */\n    public function hasScope(\n        string $scope,\n        ?CredentialEntityContract $credential = null,\n    ): bool;\n\n    /**\n     * Returns whether there is a currently authenticated user for the guard.\n     */\n    public function hasUser(): bool;\n\n    /**\n     * Returns the id of the currently authenticated user for the guard, if available.\n     */\n    public function id(): int | string | null;\n\n    /**\n     * Returns whether the user is currently impersonating another user.\n     */\n    public function isImpersonating(): bool;\n\n    /**\n     * Sets the currently authenticated user for the guard.\n     *\n     * @param null|CredentialEntityContract $credential Optional. The credential to use.\n     */\n    public function login(\n        ?CredentialEntityContract $credential,\n    ): ?self;\n\n    /**\n     * Clears the currently authenticated user for the guard. If a credential is set, it will be cleared. If a session is set, it will be cleared.\n     */\n    public function logout(): self;\n\n    /**\n     * Get an Auth0 Management API instance.\n     */\n    public function management(): ManagementInterface;\n\n    /**\n     * Processes a JWT token and returns the decoded token, or null if the token is invalid.\n     *\n     * @param string $token The JWT token to process.\n     *\n     * @return null|array<mixed>\n     */\n    public function processToken(\n        string $token,\n    ): ?array;\n\n    /**\n     * Query the /userinfo endpoint and update the currently authenticated user for the guard.\n     */\n    public function refreshUser(): void;\n\n    /**\n     * Get an Auth0 PHP SDK instance.\n     *\n     * @param bool $reset Optional. Whether to reset the SDK instance.\n     *\n     * @throws BindingResolutionException  If the Auth0 class cannot be resolved.\n     * @throws NotFoundExceptionInterface  If the Auth0 service cannot be found.\n     * @throws ContainerExceptionInterface If the Auth0 service cannot be resolved.\n     *\n     * @return Auth0Interface Auth0 PHP SDK instance.\n     */\n    public function sdk(\n        bool $reset = false,\n    ): Auth0Interface;\n\n    /**\n     * Sets the guard's currently configured credential.\n     *\n     * @param null|CredentialEntityContract $credential Optional. The credential to assign.\n     */\n    public function setCredential(?CredentialEntityContract $credential = null): self;\n\n    /**\n     * Toggle the Guard's impersonation state. This should only be used by the Impersonate trait, and is not intended for use by end-users. It is public to allow for testing.\n     *\n     * @param CredentialEntityContract $credential\n     */\n    public function setImpersonating(\n        CredentialEntityContract $credential,\n    ): self;\n\n    /**\n     * Sets the currently authenticated user for the guard. This method will replace the current user of an existing credential, if one is set, or establish a new one. If an existing credential uses a session source, the session will be updated.\n     *\n     * @param Authenticatable $user The user to set as authenticated.\n     */\n    public function setUser(\n        Authenticatable $user,\n    ): self;\n\n    /**\n     * Stop impersonating a user.\n     */\n    public function stopImpersonating(): void;\n\n    /**\n     * Returns the currently authenticated user for the guard, if available.\n     */\n    public function user(): ?Authenticatable;\n\n    /**\n     * This method is not currently implemented, but is required by Laravel's Guard contract.\n     *\n     * @param array<mixed> $credentials\n     */\n    public function validate(\n        array $credentials = [],\n    ): bool;\n\n    /**\n     * This method is not currently implemented, but is required by Laravel's Guard contract.\n     */\n    public function viaRemember(): bool;\n}\n"
  },
  {
    "path": "src/Middleware/AuthenticateMiddleware.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthenticationGuard. Use Laravel's standard `auth` middleware instead.\n *\n * @api\n */\nfinal class AuthenticateMiddleware extends AuthenticateMiddlewareAbstract implements AuthenticateMiddlewareContract\n{\n}\n"
  },
  {
    "path": "src/Middleware/AuthenticateMiddlewareAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\Laravel\\Events;\nuse Auth0\\Laravel\\Events\\Middleware\\StatefulMiddlewareRequest;\nuse Auth0\\Laravel\\Guards\\GuardContract;\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthenticationGuard. Use Laravel's standard `auth` middleware instead.\n *\n * @api\n */\nabstract class AuthenticateMiddlewareAbstract extends MiddlewareAbstract\n{\n    /**\n     * @psalm-suppress UndefinedInterfaceMethod\n     *\n     * @param Request $request\n     * @param Closure $next\n     * @param string  $scope\n     */\n    final public function handle(\n        Request $request,\n        Closure $next,\n        string $scope = '',\n    ): Response {\n        $guard = auth()->guard();\n        $scope = trim($scope);\n\n        if (! $guard instanceof GuardContract) {\n            abort(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error');\n        }\n\n        Events::dispatch(new StatefulMiddlewareRequest($request, $guard));\n\n        $credential = $guard->find(GuardContract::SOURCE_SESSION);\n\n        if ($credential instanceof CredentialEntityContract) {\n            if ('' === $scope || $guard->hasScope($scope, $credential)) {\n                $guard->login($credential);\n\n                return $next($request);\n            }\n\n            abort(Response::HTTP_FORBIDDEN, 'Forbidden');\n        }\n\n        return redirect()\n            ->setIntendedUrl($request->fullUrl())\n            ->to('/login'); // @phpstan-ignore-line\n    }\n}\n"
  },
  {
    "path": "src/Middleware/AuthenticateMiddlewareContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthenticationGuard. Use Laravel's standard `auth` middleware instead.\n *\n * @api\n */\ninterface AuthenticateMiddlewareContract extends MiddlewareContract\n{\n    /**\n     * Handle an incoming request.\n     *\n     * @param Request $request\n     * @param Closure $next\n     * @param string  $scope\n     */\n    public function handle(\n        Request $request,\n        Closure $next,\n        string $scope = '',\n    ): Response;\n}\n"
  },
  {
    "path": "src/Middleware/AuthenticateOptionalMiddleware.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthenticationGuard.\n *\n * @api\n */\nfinal class AuthenticateOptionalMiddleware extends AuthenticateOptionalMiddlewareAbstract implements AuthenticateOptionalMiddlewareContract\n{\n}\n"
  },
  {
    "path": "src/Middleware/AuthenticateOptionalMiddlewareAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\Laravel\\Events;\nuse Auth0\\Laravel\\Events\\Middleware\\StatefulMiddlewareRequest;\nuse Auth0\\Laravel\\Guards\\GuardContract;\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthenticationGuard.\n *\n * @api\n */\nabstract class AuthenticateOptionalMiddlewareAbstract extends MiddlewareAbstract\n{\n    /**\n     * @psalm-suppress UndefinedInterfaceMethod\n     *\n     * @param Request $request\n     * @param Closure $next\n     * @param string  $scope\n     */\n    final public function handle(\n        Request $request,\n        Closure $next,\n        string $scope = '',\n    ): Response {\n        $guard = auth()->guard();\n\n        if (! $guard instanceof GuardContract) {\n            abort(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error');\n        }\n\n        Events::dispatch(new StatefulMiddlewareRequest($request, $guard));\n\n        $credential = $guard->find(GuardContract::SOURCE_SESSION);\n\n        if ($credential instanceof CredentialEntityContract && ('' === $scope || $guard->hasScope($scope, $credential))) {\n            $guard->login($credential);\n        }\n\n        return $next($request);\n    }\n}\n"
  },
  {
    "path": "src/Middleware/AuthenticateOptionalMiddlewareContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthenticationGuard.\n *\n * @api\n */\ninterface AuthenticateOptionalMiddlewareContract extends MiddlewareContract\n{\n    /**\n     * Handle an incoming request.\n     *\n     * @param Request $request\n     * @param Closure $next\n     * @param string  $scope\n     */\n    public function handle(\n        Request $request,\n        Closure $next,\n        string $scope = '',\n    ): Response;\n}\n"
  },
  {
    "path": "src/Middleware/AuthenticatorMiddleware.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * Attaches the Auth0 session authenticator as the guard for requests.\n *\n * @api\n */\nfinal class AuthenticatorMiddleware extends MiddlewareAbstract implements AuthenticatorMiddlewareContract\n{\n    public function handle(\n        Request $request,\n        Closure $next,\n    ): Response {\n        auth()->shouldUse('auth0-session');\n\n        return $next($request);\n    }\n}\n"
  },
  {
    "path": "src/Middleware/AuthenticatorMiddlewareContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\n/**\n * @api\n */\ninterface AuthenticatorMiddlewareContract extends MiddlewareContract\n{\n}\n"
  },
  {
    "path": "src/Middleware/AuthorizeMiddleware.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthorizationGuard. Use Laravel's standard `auth` middleware instead.\n *\n * @api\n */\nfinal class AuthorizeMiddleware extends AuthorizeMiddlewareAbstract implements AuthorizeMiddlewareContract\n{\n}\n"
  },
  {
    "path": "src/Middleware/AuthorizeMiddlewareAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\Laravel\\Events;\nuse Auth0\\Laravel\\Events\\Middleware\\StatelessMiddlewareRequest;\nuse Auth0\\Laravel\\Guards\\GuardContract;\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthorizationGuard. Use Laravel's standard `auth` middleware instead.\n *\n * @api\n */\nabstract class AuthorizeMiddlewareAbstract extends MiddlewareAbstract\n{\n    /**\n     * @psalm-suppress UndefinedInterfaceMethod\n     *\n     * @param Request $request\n     * @param Closure $next\n     * @param string  $scope\n     */\n    final public function handle(\n        Request $request,\n        Closure $next,\n        string $scope = '',\n    ): Response {\n        $guard = auth()->guard();\n\n        if (! $guard instanceof GuardContract) {\n            return $next($request);\n        }\n\n        Events::dispatch(new StatelessMiddlewareRequest($request, $guard));\n\n        $credential = $guard->find(GuardContract::SOURCE_TOKEN);\n\n        if ($credential instanceof CredentialEntityContract) {\n            if ('' === $scope || $guard->hasScope($scope, $credential)) {\n                $guard->login($credential);\n\n                return $next($request);\n            }\n\n            abort(Response::HTTP_FORBIDDEN, 'Forbidden');\n        }\n\n        abort(Response::HTTP_UNAUTHORIZED, 'Unauthorized');\n    }\n}\n"
  },
  {
    "path": "src/Middleware/AuthorizeMiddlewareContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthorizationGuard. Use Laravel's standard `auth` middleware instead.\n *\n * @api\n */\ninterface AuthorizeMiddlewareContract extends MiddlewareContract\n{\n    /**\n     * Handle an incoming request.\n     *\n     * @param Request $request\n     * @param Closure $next\n     * @param string  $scope\n     */\n    public function handle(\n        Request $request,\n        Closure $next,\n        string $scope = '',\n    ): Response;\n}\n"
  },
  {
    "path": "src/Middleware/AuthorizeOptionalMiddleware.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthorizationGuard.\n *\n * @api\n */\nfinal class AuthorizeOptionalMiddleware extends AuthorizeOptionalMiddlewareAbstract implements AuthorizeOptionalMiddlewareContract\n{\n}\n"
  },
  {
    "path": "src/Middleware/AuthorizeOptionalMiddlewareAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\Laravel\\Events;\nuse Auth0\\Laravel\\Events\\Middleware\\StatelessMiddlewareRequest;\nuse Auth0\\Laravel\\Guards\\GuardContract;\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthorizationGuard.\n *\n * @api\n */\nabstract class AuthorizeOptionalMiddlewareAbstract extends MiddlewareAbstract\n{\n    /**\n     * @psalm-suppress UndefinedInterfaceMethod\n     *\n     * @param Request $request\n     * @param Closure $next\n     * @param string  $scope\n     */\n    final public function handle(\n        Request $request,\n        Closure $next,\n        string $scope = '',\n    ): Response {\n        $guard = auth()->guard();\n\n        if (! $guard instanceof GuardContract) {\n            return $next($request);\n        }\n\n        Events::dispatch(new StatelessMiddlewareRequest($request, $guard));\n\n        $credential = $guard->find(GuardContract::SOURCE_TOKEN);\n\n        if ($credential instanceof CredentialEntityContract && ('' === $scope || $guard->hasScope($scope, $credential))) {\n            $guard->login($credential);\n\n            return $next($request);\n        }\n\n        return $next($request);\n    }\n}\n"
  },
  {
    "path": "src/Middleware/AuthorizeOptionalMiddlewareContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * @deprecated 7.8.0 This middleware is no longer necessary when using Auth0\\Laravel\\Guards\\AuthorizationGuard.\n *\n * @api\n */\ninterface AuthorizeOptionalMiddlewareContract extends MiddlewareContract\n{\n    /**\n     * Handle an incoming request.\n     *\n     * @param Request $request\n     * @param Closure $next\n     * @param string  $scope\n     */\n    public function handle(\n        Request $request,\n        Closure $next,\n        string $scope = '',\n    ): Response;\n}\n"
  },
  {
    "path": "src/Middleware/AuthorizerMiddleware.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * Attaches the Auth0 token authorizer as the guard for requests.\n *\n * @api\n */\nfinal class AuthorizerMiddleware extends MiddlewareAbstract implements AuthorizerMiddlewareContract\n{\n    public function handle(\n        Request $request,\n        Closure $next,\n    ): Response {\n        auth()->shouldUse('auth0-api');\n\n        return $next($request);\n    }\n}\n"
  },
  {
    "path": "src/Middleware/AuthorizerMiddlewareContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\n/**\n * @api\n */\ninterface AuthorizerMiddlewareContract extends MiddlewareContract\n{\n}\n"
  },
  {
    "path": "src/Middleware/GuardMiddleware.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\n/**\n * Assigns a specific guard to the request.\n *\n * @api\n */\nfinal class GuardMiddleware extends GuardMiddlewareAbstract implements GuardMiddlewareContract\n{\n}\n"
  },
  {
    "path": "src/Middleware/GuardMiddlewareAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nuse function is_string;\n\n/**\n * @api\n */\nabstract class GuardMiddlewareAbstract extends MiddlewareAbstract\n{\n    protected string $defaultGuard = '';\n\n    public function __construct()\n    {\n        $guard = config('auth.defaults.guard');\n\n        if (is_string($guard)) {\n            $this->defaultGuard = $guard;\n        }\n    }\n\n    final public function handle(\n        Request $request,\n        Closure $next,\n        ?string $guard = null,\n    ): Response {\n        $guard = trim($guard ?? '');\n\n        if ('' === $guard) {\n            $guard = $this->defaultGuard;\n        }\n\n        auth()->shouldUse($guard);\n\n        return $next($request);\n    }\n}\n"
  },
  {
    "path": "src/Middleware/GuardMiddlewareContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\n/**\n * @api\n */\ninterface GuardMiddlewareContract extends MiddlewareContract\n{\n}\n"
  },
  {
    "path": "src/Middleware/MiddlewareAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\nuse Auth0\\Laravel\\Guards\\{AuthenticationGuardContract, AuthorizationGuardContract};\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\n/**\n * @codeCoverageIgnore\n *\n * @internal\n *\n * @api\n */\nabstract class MiddlewareAbstract\n{\n    final public function getAuthenticationGuard(\n        ?string $guard = null,\n    ): AuthenticationGuardContract {\n        return app('auth0.authenticator');\n    }\n\n    final public function getAuthorizationGuard(\n        ?string $guard = null,\n    ): AuthorizationGuardContract {\n        return app('auth0.authorizer');\n    }\n\n    abstract public function handle(\n        Request $request,\n        Closure $next,\n    ): Response;\n}\n"
  },
  {
    "path": "src/Middleware/MiddlewareContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Middleware;\n\n/**\n * @internal\n *\n * @api\n */\ninterface MiddlewareContract\n{\n}\n"
  },
  {
    "path": "src/Service.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Entities\\InstanceEntityTrait;\n\n/**\n * Auth0 Laravel SDK service provider. Provides access to the SDK's methods.\n *\n * @api\n */\nfinal class Service extends ServiceAbstract implements ServiceContract\n{\n    use InstanceEntityTrait;\n}\n"
  },
  {
    "path": "src/ServiceAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Controllers\\{CallbackController, LoginController, LogoutController};\nuse Auth0\\Laravel\\Entities\\InstanceEntityAbstract;\nuse Illuminate\\Support\\Facades\\Route;\nuse Psr\\Http\\Message\\ResponseInterface;\n\nuse function in_array;\nuse function is_array;\n\n/**\n * @api\n */\nabstract class ServiceAbstract extends InstanceEntityAbstract\n{\n    /**\n     * The Laravel-Auth0 SDK version:.\n     *\n     * @var string\n     */\n    public const VERSION = '7.22.0';\n\n    /**\n     * Decode a PSR-7 HTTP Response Message containing a JSON content body to a PHP array. Returns null if the response was not successful, or the response body was not JSON.\n     *\n     * @param ResponseInterface $response\n     *\n     * @return null|array<mixed>\n     */\n    final public static function json(ResponseInterface $response): ?array\n    {\n        if (! in_array($response->getStatusCode(), [200, 201], true)) {\n            return null;\n        }\n\n        $json = json_decode((string) $response->getBody(), true);\n\n        if (! is_array($json)) {\n            return null;\n        }\n\n        return $json;\n    }\n\n    /**\n     * Register the SDK's authentication routes and controllers.\n     *\n     * @param string $authenticationGuard The name of the authentication guard to use.\n     */\n    final public static function routes(\n        string $authenticationGuard = 'auth0-session',\n    ): void {\n        Route::group(['middleware' => ['web', 'guard:' . $authenticationGuard]], static function (): void {\n            Route::get(Configuration::string(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_LOGIN) ?? '/login', LoginController::class)->name('login');\n            Route::get(Configuration::string(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_LOGOUT) ?? '/logout', LogoutController::class)->name('logout');\n            Route::get(Configuration::string(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_CALLBACK) ?? '/callback', CallbackController::class)->name('callback');\n        });\n    }\n}\n"
  },
  {
    "path": "src/ServiceContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Entities\\InstanceEntityContract;\n\ninterface ServiceContract extends InstanceEntityContract\n{\n}\n"
  },
  {
    "path": "src/ServiceProvider.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\n/**\n * @api\n */\nfinal class ServiceProvider extends ServiceProviderAbstract implements ServiceProviderContract\n{\n}\n"
  },
  {
    "path": "src/ServiceProviderAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\Bridges\\{CacheBridge, CacheItemBridge, SessionBridge};\nuse Auth0\\Laravel\\Controllers\\{CallbackController, LoginController, LogoutController};\nuse Auth0\\Laravel\\Guards\\{AuthenticationGuard, AuthorizationGuard, GuardContract};\nuse Auth0\\Laravel\\Middleware\\{AuthenticateMiddleware, AuthenticateOptionalMiddleware, AuthenticatorMiddleware, AuthorizeMiddleware, AuthorizeOptionalMiddleware, AuthorizerMiddleware, GuardMiddleware};\nuse Illuminate\\Auth\\Access\\Response;\nuse Illuminate\\Auth\\AuthManager;\nuse Illuminate\\Contracts\\Auth\\Access\\Gate;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Contracts\\Foundation\\Application;\nuse Illuminate\\Contracts\\Http\\Kernel;\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\ServiceProvider;\n\nuse function is_string;\n\n/**\n * @api\n */\nabstract class ServiceProviderAbstract extends ServiceProvider\n{\n    final public function boot(\n        Router $router,\n        AuthManager $auth,\n        Gate $gate,\n    ): self {\n        $this->mergeConfigFrom(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'config', 'auth0.php']), 'auth0');\n        $this->publishes([implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'config', 'auth0.php']) => config_path('auth0.php')], 'auth0');\n\n        $auth->extend('auth0.authenticator', fn (Application $app, string $name, array $config): AuthenticationGuard => new AuthenticationGuard($name, $config));\n        $auth->extend('auth0.authorizer', fn (Application $app, string $name, array $config): AuthorizationGuard => new AuthorizationGuard($name, $config));\n        $auth->provider('auth0.provider', static fn (Application $app, array $config): UserProvider => new UserProvider($config));\n\n        $router->aliasMiddleware('guard', GuardMiddleware::class);\n\n        $gate->define('scope', static function (Authenticatable $user, string $scope, ?GuardContract $guard = null): bool {\n            $guard ??= auth()->guard();\n\n            if (! $guard instanceof GuardContract) {\n                return false;\n            }\n\n            return $guard->hasScope($scope);\n        });\n\n        $gate->define('permission', static function (Authenticatable $user, string $permission, ?GuardContract $guard = null): bool {\n            $guard ??= auth()->guard();\n\n            if (! $guard instanceof GuardContract) {\n                return false;\n            }\n\n            return $guard->hasPermission($permission);\n        });\n\n        $gate->before(static function (?Authenticatable $user, ?string $ability) {\n            $guard = auth()->guard();\n\n            if (! $guard instanceof GuardContract || ! $user instanceof Authenticatable || ! is_string($ability)) {\n                return;\n            }\n\n            if (str_starts_with($ability, 'scope:')) {\n                if ($guard->hasScope(substr($ability, 6))) {\n                    return Response::allow();\n                }\n\n                return Response::deny();\n            }\n\n            if (str_contains($ability, ':')) {\n                if ($guard->hasPermission($ability)) {\n                    return Response::allow();\n                }\n\n                return Response::deny();\n            }\n        });\n\n        $this->registerDeprecated($router, $auth);\n        $this->registerMiddleware($router);\n        $this->registerRoutes();\n\n        return $this;\n    }\n\n    final public function provides()\n    {\n        return [\n            Auth0::class,\n            AuthenticateMiddleware::class,\n            AuthenticateOptionalMiddleware::class,\n            AuthenticationGuard::class,\n            AuthenticatorMiddleware::class,\n            AuthorizationGuard::class,\n            AuthorizeMiddleware::class,\n            AuthorizeOptionalMiddleware::class,\n            AuthorizerMiddleware::class,\n            CacheBridge::class,\n            CacheItemBridge::class,\n            CallbackController::class,\n            Configuration::class,\n            Guard::class,\n            GuardMiddleware::class,\n            LoginController::class,\n            LogoutController::class,\n            Service::class,\n            SessionBridge::class,\n            UserProvider::class,\n            UserRepository::class,\n        ];\n    }\n\n    final public function register(): self\n    {\n        $this->registerGuards();\n\n        $this->app->singleton(Auth0::class, static fn (): Service => new Service());\n        $this->app->singleton(Service::class, static fn (): Service => new Service());\n        $this->app->singleton(Configuration::class, static fn (): Configuration => new Configuration());\n        $this->app->singleton(Service::class, static fn (): Service => new Service());\n        $this->app->singleton(AuthenticatorMiddleware::class, static fn (): AuthenticatorMiddleware => new AuthenticatorMiddleware());\n        $this->app->singleton(AuthorizerMiddleware::class, static fn (): AuthorizerMiddleware => new AuthorizerMiddleware());\n        $this->app->singleton(AuthenticateMiddleware::class, static fn (): AuthenticateMiddleware => new AuthenticateMiddleware());\n        $this->app->singleton(AuthenticateOptionalMiddleware::class, static fn (): AuthenticateOptionalMiddleware => new AuthenticateOptionalMiddleware());\n        $this->app->singleton(AuthorizeMiddleware::class, static fn (): AuthorizeMiddleware => new AuthorizeMiddleware());\n        $this->app->singleton(AuthorizeOptionalMiddleware::class, static fn (): AuthorizeOptionalMiddleware => new AuthorizeOptionalMiddleware());\n        $this->app->singleton(GuardMiddleware::class, static fn (): GuardMiddleware => new GuardMiddleware());\n        $this->app->singleton(CallbackController::class, static fn (): CallbackController => new CallbackController());\n        $this->app->singleton(LoginController::class, static fn (): LoginController => new LoginController());\n        $this->app->singleton(LogoutController::class, static fn (): LogoutController => new LogoutController());\n        $this->app->singleton(UserProvider::class, static fn (): UserProvider => new UserProvider());\n        $this->app->singleton(UserRepository::class, static fn (): UserRepository => new UserRepository());\n\n        $this->app->singleton('auth0', static fn (): Service => app(Service::class));\n        $this->app->singleton('auth0.repository', static fn (): UserRepository => app(UserRepository::class));\n\n        return $this;\n    }\n\n    final public function registerDeprecated(\n        Router $router,\n        AuthManager $auth,\n    ): void {\n        $auth->extend('auth0.guard', fn (Application $app, string $name, array $config): Guard => new Guard($name, $config));\n\n        $router->aliasMiddleware('auth0.authenticate.optional', AuthenticateOptionalMiddleware::class);\n        $router->aliasMiddleware('auth0.authenticate', AuthenticateMiddleware::class);\n        $router->aliasMiddleware('auth0.authorize.optional', AuthorizeOptionalMiddleware::class);\n        $router->aliasMiddleware('auth0.authorize', AuthorizeMiddleware::class);\n    }\n\n    /**\n     * @codeCoverageIgnore\n     */\n    final public function registerGuards(): void\n    {\n        if (true === config('auth0.registerGuards')) {\n            if (null === config('auth.guards.auth0-session')) {\n                config([\n                    'auth.guards.auth0-session' => [\n                        'driver' => 'auth0.authenticator',\n                        'configuration' => 'web',\n                        'provider' => 'auth0-provider',\n                    ],\n                ]);\n            }\n\n            if (null === config('auth.guards.auth0-api')) {\n                config([\n                    'auth.guards.auth0-api' => [\n                        'driver' => 'auth0.authorizer',\n                        'configuration' => 'api',\n                        'provider' => 'auth0-provider',\n                    ],\n                ]);\n            }\n\n            if (null === config('auth.providers.auth0-provider')) {\n                config([\n                    'auth.providers.auth0-provider' => [\n                        'driver' => 'auth0.provider',\n                        'repository' => 'auth0.repository',\n                    ],\n                ]);\n            }\n        }\n    }\n\n    /**\n     * @codeCoverageIgnore\n     *\n     * @param Router $router\n     */\n    final public function registerMiddleware(\n        Router $router,\n    ): void {\n        if (true === config('auth0.registerMiddleware')) {\n            $kernel = $this->app->make(Kernel::class);\n\n            /**\n             * @var \\Illuminate\\Foundation\\Http\\Kernel $kernel\n             */\n            $kernel->appendMiddlewareToGroup('web', AuthenticatorMiddleware::class);\n            $kernel->prependToMiddlewarePriority(AuthenticatorMiddleware::class);\n\n            $kernel->appendMiddlewareToGroup('api', AuthorizerMiddleware::class);\n            $kernel->prependToMiddlewarePriority(AuthorizerMiddleware::class);\n        }\n    }\n\n    final public function registerRoutes(): void\n    {\n        if (true === config('auth0.registerAuthenticationRoutes')) {\n            Route::group(['middleware' => 'web'], static function (): void {\n                Route::get(Configuration::string(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_LOGIN) ?? '/login', LoginController::class)->name('login');\n                Route::get(Configuration::string(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_LOGOUT) ?? '/logout', LogoutController::class)->name('logout');\n                Route::get(Configuration::string(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_CALLBACK) ?? '/callback', CallbackController::class)->name('callback');\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/ServiceProviderContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Illuminate\\Auth\\AuthManager;\nuse Illuminate\\Routing\\Router;\n\n/**\n * @api\n */\ninterface ServiceProviderContract\n{\n    /**\n     * Register migration helpers for deprecated classes.\n     *\n     * @param Router      $router\n     * @param AuthManager $auth\n     */\n    public function registerDeprecated(\n        Router $router,\n        AuthManager $auth,\n    ): void;\n\n    /**\n     * Register the Auth0 guards.\n     */\n    public function registerGuards(): void;\n\n    /**\n     * Register the Auth0 service middleware.\n     *\n     * @param Router $router\n     */\n    public function registerMiddleware(\n        Router $router,\n    ): void;\n\n    /**\n     * Register the Auth0 authentication routes.\n     */\n    public function registerRoutes(): void;\n}\n"
  },
  {
    "path": "src/Traits/ActingAsAuth0User.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Traits;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntity;\nuse Auth0\\Laravel\\Guards\\GuardContract;\nuse Auth0\\Laravel\\UserProvider;\nuse Auth0\\Laravel\\Users\\ImposterUser;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\n/**\n * Set the currently logged in user for the application. Only intended for unit testing.\n *\n * @deprecated 7.8.0 Use Auth0\\Laravel\\Traits\\Impersonate instead.\n *\n * @api\n */\ntrait ActingAsAuth0User\n{\n    public array $defaultActingAsAttributes = [\n        'sub' => 'some-auth0-user-id',\n        'azp' => 'some-auth0-application-client-id',\n        'scope' => '',\n    ];\n\n    /**\n     * Set the currently logged in user for the application. Only intended for unit testing.\n     *\n     * @param array<mixed> $attributes The attributes to use for the user.\n     * @param null|string  $guard      The guard to impersonate with.\n     * @param ?int         $source\n     */\n    public function actingAsAuth0User(\n        array $attributes = [],\n        ?string $guard = null,\n        ?int $source = GuardContract::SOURCE_TOKEN,\n    ): self {\n        $issued = time();\n        $expires = $issued + 60 * 60;\n        $timestamps = ['iat' => $issued, 'exp' => $expires];\n        $attributes = array_merge($this->defaultActingAsAttributes, $timestamps, $attributes);\n        $scope = $attributes['scope'] ? explode(' ', $attributes['scope']) : [];\n        unset($attributes['scope']);\n\n        $instance = auth()->guard($guard);\n\n        if (! $instance instanceof GuardContract) {\n            $user = new ImposterUser($attributes);\n\n            return $this->actingAs($user, $guard);\n        }\n\n        $provider = new UserProvider();\n\n        if (GuardContract::SOURCE_SESSION === $source) {\n            $user = $provider->getRepository()->fromSession($attributes);\n        } else {\n            $user = $provider->getRepository()->fromAccessToken($attributes);\n        }\n\n        $credential = CredentialEntity::create(\n            user: $user,\n            accessTokenScope: $scope,\n        );\n\n        $instance->setImpersonating($credential, $source);\n\n        return $this->actingAs($user, $guard);\n    }\n\n    abstract public function actingAs(Authenticatable $user, $guard = null);\n}\n"
  },
  {
    "path": "src/Traits/Impersonate.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Traits;\n\nuse Auth0\\Laravel\\Entities\\CredentialEntityContract;\nuse Auth0\\Laravel\\Guards\\GuardContract;\nuse Auth0\\Laravel\\Users\\ImposterUser;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\n/**\n * Pretend to be an authenticated user or a bearer token-established stateless user for the request. Only intended for unit testing.\n *\n * @api\n */\ntrait Impersonate\n{\n    /**\n     * Pretend to be an authenticated user or a bearer token-established stateless user for the request. Only intended for unit testing.\n     *\n     * @param CredentialEntityContract $credential The Credential to impersonate.\n     * @param null|int                 $source     The source of the Credential.\n     * @param null|string              $guard      The guard to impersonate with.\n     *\n     * @return $this The current test case instance.\n     */\n    public function impersonate(\n        CredentialEntityContract $credential,\n        ?int $source = null,\n        ?string $guard = null,\n    ): self {\n        if (GuardContract::SOURCE_SESSION === $source || null === $source) {\n            $this->impersonateSession($credential, $guard);\n        }\n\n        if (GuardContract::SOURCE_TOKEN === $source || null === $source) {\n            $this->impersonateToken($credential, $guard);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Pretend to be an authenticated user for the request. Only intended for unit testing.\n     *\n     * @param CredentialEntityContract $credential The Credential to impersonate.\n     * @param null|string              $guard      The guard to impersonate with.\n     *\n     * @return $this The current test case instance.\n     */\n    public function impersonateSession(\n        CredentialEntityContract $credential,\n        ?string $guard = null,\n    ): self {\n        $instance = auth()->guard($guard);\n        $user = $credential->getUser() ?? new ImposterUser([]);\n\n        if ($instance instanceof GuardContract) {\n            $instance->setImpersonating($credential, GuardContract::SOURCE_SESSION);\n        }\n\n        return $this->actingAs($user, $guard);\n    }\n\n    /**\n     * Pretend to be a bearer token-established stateless user for the request. Only intended for unit testing.\n     *\n     * @param CredentialEntityContract $credential The Credential to impersonate.\n     * @param null|string              $guard      The guard to impersonate with.\n     *\n     * @return $this The current test case instance.\n     */\n    public function impersonateToken(\n        CredentialEntityContract $credential,\n        ?string $guard = null,\n    ): self {\n        $instance = auth()->guard($guard);\n        $user = $credential->getUser() ?? new ImposterUser([]);\n\n        if ($instance instanceof GuardContract) {\n            $instance->setImpersonating($credential, GuardContract::SOURCE_TOKEN);\n        }\n\n        return $this->actingAs($user, $guard);\n    }\n\n    abstract public function actingAs(Authenticatable $user, $guard = null);\n}\n"
  },
  {
    "path": "src/UserProvider.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\n/**\n * User provider for the Auth0 user repository.\n *\n * @internal\n *\n * @api\n */\nfinal class UserProvider extends UserProviderAbstract implements UserProviderContract\n{\n}\n"
  },
  {
    "path": "src/UserProviderAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Guards\\GuardContract;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Contracts\\Container\\BindingResolutionException;\nuse Illuminate\\Support\\Facades\\Cache;\n\nuse function is_string;\nuse function sprintf;\n\n/**\n * User provider for the Auth0 user repository.\n *\n * @api\n */\nabstract class UserProviderAbstract\n{\n    /**\n     * @var string\n     */\n    protected const TELESCOPE = '\\Laravel\\Telescope\\Telescope';\n\n    protected ?UserRepositoryContract $repository = null;\n\n    protected string $repositoryName = '';\n\n    public function __construct(\n        protected array $config = [],\n    ) {\n    }\n\n    final public function getRepository(): UserRepositoryContract\n    {\n        return $this->repository ?? $this->resolveRepository();\n    }\n\n    /**\n     * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter\n     *\n     * @param Authenticatable $user\n     * @param array           $credentials\n     * @param bool            $force\n     *\n     * @codeCoverageIgnore\n     */\n    final public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false): void\n    {\n    }\n\n    /**\n     * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter\n     *\n     * @param array $credentials\n     */\n    final public function retrieveByCredentials(array $credentials): ?Authenticatable\n    {\n        if ([] === $credentials) {\n            return null;\n        }\n\n        $hash = hash('sha256', json_encode($credentials, JSON_THROW_ON_ERROR) ?: ''); /** @phpstan-ignore-line */\n        $cached = $this->withoutRecording(static fn (): mixed => Cache::get('auth0_sdk_credential_lookup_' . $hash));\n\n        if ($cached instanceof Authenticatable) {\n            return $cached;\n        }\n\n        static $lastResponse = null;\n        static $lastCredentials = null;\n\n        // @codeCoverageIgnoreStart\n\n        /**\n         * @var ?Authenticatable $lastResponse\n         * @var array            $lastCredentials\n         */\n        if ($lastCredentials === $credentials) {\n            return $lastResponse;\n        }\n\n        if (class_exists(self::TELESCOPE) && true === config('telescope.enabled')) {\n            static $depth = 0;\n            static $lastCalled = null;\n\n            /**\n             * @var int  $depth\n             * @var ?int $lastCalled\n             */\n            if (null === $lastCalled) {\n                $lastCalled = time();\n            }\n\n            if ($lastCredentials !== $credentials || time() - $lastCalled > 10) {\n                $lastResponse = null;\n                $depth = 0;\n            }\n\n            if ($depth >= 1) {\n                return $lastResponse;\n            }\n\n            ++$depth;\n            $lastCalled = time();\n            $lastCredentials = $credentials;\n        }\n\n        // @codeCoverageIgnoreEnd\n\n        $lastResponse = $this->getRepository()->fromSession($credentials);\n\n        $this->withoutRecording(static fn (): bool => Cache::put('auth0_sdk_credential_lookup_' . $hash, $lastResponse, 5));\n\n        return $lastResponse;\n    }\n\n    /**\n     * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter\n     *\n     * @codeCoverageIgnore\n     *\n     * @param mixed $identifier\n     */\n    final public function retrieveById($identifier): ?Authenticatable\n    {\n        return null;\n    }\n\n    /**\n     * @psalm-suppress DocblockTypeContradiction\n     *\n     * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter\n     *\n     * @param mixed $identifier\n     * @param mixed $token\n     */\n    final public function retrieveByToken($identifier, $token): ?Authenticatable\n    {\n        // @phpstan-ignore-next-line\n        if (! is_string($token)) {\n            return null;\n        }\n\n        $guard = auth()->guard();\n\n        if (! $guard instanceof GuardContract) {\n            return null;\n        }\n\n        $user = $guard->processToken($token);\n\n        return null !== $user ? $this->getRepository()->fromAccessToken($user) : null;\n    }\n\n    final public function setRepository(string $repository): void\n    {\n        $this->resolveRepository($repository);\n    }\n\n    /**\n     * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter\n     *\n     * @codeCoverageIgnore\n     *\n     * @param Authenticatable $user\n     * @param mixed           $token\n     */\n    final public function updateRememberToken(Authenticatable $user, $token): void\n    {\n    }\n\n    /**\n     * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter\n     *\n     * @param Authenticatable $user\n     * @param array           $credentials\n     */\n    final public function validateCredentials(\n        Authenticatable $user,\n        array $credentials,\n    ): bool {\n        return false;\n    }\n\n    protected function getConfiguration(\n        string $key,\n    ): array | string | null {\n        return $this->config[$key] ?? null;\n    }\n\n    protected function getRepositoryName(): string\n    {\n        return $this->repositoryName;\n    }\n\n    protected function resolveRepository(\n        ?string $repositoryName = null,\n    ): UserRepositoryContract {\n        $model = $repositoryName;\n        $model ??= $this->getConfiguration('model');\n        $model ??= $this->getConfiguration('repository');\n        $model ??= UserRepository::class;\n\n        if ($model === $this->getRepositoryName()) {\n            return $this->getRepository();\n        }\n\n        if (! is_string($model)) {\n            throw new BindingResolutionException('The configured Repository could not be loaded.');\n        }\n\n        if (! app()->bound($model)) {\n            try {\n                app()->make($model);\n            } catch (BindingResolutionException) {\n                throw new BindingResolutionException(sprintf('The configured Repository %s could not be loaded.', $model));\n            }\n        }\n\n        $this->setRepositoryName($model);\n\n        /**\n         * @var UserRepositoryContract $repository\n         */\n        $repository = app($model);\n\n        return $this->repository = $repository;\n    }\n\n    protected function setConfiguration(\n        string $key,\n        string $value,\n    ): void {\n        $this->config[$key] = $value;\n    }\n\n    protected function setRepositoryName(string $repositoryName): void\n    {\n        $this->setConfiguration('model', $repositoryName);\n        $this->repositoryName = $repositoryName;\n    }\n\n    /**\n     * @codeCoverageIgnore\n     *\n     * @param callable $callback\n     */\n    protected function withoutRecording(callable $callback): mixed\n    {\n        if (class_exists(self::TELESCOPE)) {\n            return self::TELESCOPE::withoutRecording($callback);\n        }\n\n        return $callback();\n    }\n}\n"
  },
  {
    "path": "src/UserProviderContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Illuminate\\Contracts\\Auth\\UserProvider;\n\n/**\n * @api\n */\ninterface UserProviderContract extends UserProvider\n{\n    /**\n     * Returns the assigned user provider.\n     */\n    public function getRepository(): UserRepositoryContract;\n}\n"
  },
  {
    "path": "src/UserRepository.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Auth0\\Laravel\\Users\\{StatefulUser, StatelessUser};\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\n/**\n * User repository for the Auth0 Laravel SDK user provider.\n *\n * @internal\n *\n * @api\n */\nfinal class UserRepository extends UserRepositoryAbstract implements UserRepositoryContract\n{\n    public function fromAccessToken(array $user): ?Authenticatable\n    {\n        return new StatelessUser($user);\n    }\n\n    public function fromSession(array $user): ?Authenticatable\n    {\n        return new StatefulUser($user);\n    }\n}\n"
  },
  {
    "path": "src/UserRepositoryAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\n/**\n * User repository for the Auth0 Laravel SDK user provider.\n *\n * @api\n */\nabstract class UserRepositoryAbstract\n{\n    abstract public function fromAccessToken(array $user): ?Authenticatable;\n\n    abstract public function fromSession(array $user): ?Authenticatable;\n}\n"
  },
  {
    "path": "src/UserRepositoryContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel;\n\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\n/**\n * @api\n */\ninterface UserRepositoryContract\n{\n    /**\n     * Generate a stateless User instance from a parsed Access Token.\n     *\n     * @param array $user an array containing the raw Auth0 user data\n     */\n    public function fromAccessToken(array $user): ?Authenticatable;\n\n    /**\n     * Generate a stateful User instance from an available Auth0-PHP user session.\n     *\n     * @param array $user an array containing the raw Auth0 user data\n     */\n    public function fromSession(array $user): ?Authenticatable;\n}\n"
  },
  {
    "path": "src/Users/ImposterUser.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Users;\n\n/**\n * Model representing an \"imposter\" user, assigned using one of the unit testing traits. Only intended for unit tests.\n *\n * @api\n */\nfinal class ImposterUser extends UserAbstract implements ImposterUserContract\n{\n    use UserTrait;\n}\n"
  },
  {
    "path": "src/Users/ImposterUserContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Users;\n\n/**\n * @api\n */\ninterface ImposterUserContract extends UserContract\n{\n}\n"
  },
  {
    "path": "src/Users/StatefulUser.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Users;\n\n/**\n * Model representing a user authenticated by a session.\n *\n * @api\n */\nfinal class StatefulUser extends UserAbstract implements StatefulUserContract\n{\n    use UserTrait;\n}\n"
  },
  {
    "path": "src/Users/StatefulUserContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Users;\n\n/**\n * @api\n */\ninterface StatefulUserContract extends UserContract\n{\n}\n"
  },
  {
    "path": "src/Users/StatelessUser.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Users;\n\n/**\n * Model representing a user derived from an access token.\n *\n * @api\n */\nfinal class StatelessUser extends UserAbstract implements StatelessUserContract\n{\n    use UserTrait;\n}\n"
  },
  {
    "path": "src/Users/StatelessUserContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Users;\n\n/**\n * @api\n */\ninterface StatelessUserContract extends UserContract\n{\n}\n"
  },
  {
    "path": "src/Users/UserAbstract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Users;\n\n/**\n * @api\n */\nabstract class UserAbstract\n{\n    public function __construct(\n        protected array $attributes = [],\n    ) {\n        $this->fill($attributes);\n    }\n\n    public function __get(string $key): mixed\n    {\n        return $this->getAttribute($key);\n    }\n\n    public function __set(string $key, mixed $value): void\n    {\n        $this->setAttribute($key, $value);\n    }\n\n    final public function getAttribute(string $key, mixed $default = null): mixed\n    {\n        return $this->attributes[$key] ?? $default;\n    }\n\n    final public function getAttributes(): mixed\n    {\n        return $this->attributes;\n    }\n\n    final public function getAuthIdentifier(): int | string | null\n    {\n        return $this->attributes['sub'] ?? $this->attributes['user_id'] ?? $this->attributes['email'] ?? null;\n    }\n\n    final public function getAuthIdentifierName(): string\n    {\n        return 'id';\n    }\n\n    final public function getAuthPassword(): string\n    {\n        return '';\n    }\n\n    final public function getAuthPasswordName(): string\n    {\n        return 'password';\n    }\n\n    final public function getRememberToken(): string\n    {\n        return '';\n    }\n\n    final public function getRememberTokenName(): string\n    {\n        return '';\n    }\n\n    final public function jsonSerialize(): mixed\n    {\n        return $this->attributes;\n    }\n\n    /**\n     * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter\n     *\n     * @param mixed $value\n     */\n    final public function setRememberToken(mixed $value): void\n    {\n    }\n\n    abstract public function fill(array $attributes): self;\n\n    abstract public function setAttribute(string $key, mixed $value): self;\n}\n"
  },
  {
    "path": "src/Users/UserContract.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Users;\n\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse JsonSerializable;\n\ninterface UserContract extends Authenticatable, JsonSerializable\n{\n    /**\n     * @param array $attributes attributes representing the user data\n     */\n    public function __construct(array $attributes = []);\n\n    /**\n     * Dynamically retrieve attributes on the model.\n     *\n     * @param string $key\n     */\n    public function __get(string $key): mixed;\n\n    /**\n     * Dynamically set attributes on the model.\n     *\n     * @param mixed  $value\n     * @param string $key\n     */\n    public function __set(string $key, mixed $value): void;\n\n    /**\n     * Fill the model with an array of attributes.\n     *\n     * @param array $attributes\n     */\n    public function fill(array $attributes): self;\n\n    /**\n     * Get an attribute from the model.\n     *\n     * @param mixed  $default\n     * @param string $key\n     */\n    public function getAttribute(string $key, mixed $default = null): mixed;\n\n    /**\n     * Set a given attribute on the model.\n     *\n     * @param mixed  $value\n     * @param string $key\n     */\n    public function setAttribute(string $key, mixed $value): self;\n}\n"
  },
  {
    "path": "src/Users/UserTrait.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Users;\n\n/**\n * @api\n */\ntrait UserTrait\n{\n    final public function fill(array $attributes): self\n    {\n        foreach ($attributes as $key => $value) {\n            $this->setAttribute($key, $value);\n        }\n\n        return $this;\n    }\n\n    final public function setAttribute(string $key, mixed $value): self\n    {\n        $this->attributes[$key] = $value;\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "tests/Pest.php",
    "content": "<?php\n\nuse Auth0\\Laravel\\Tests\\TestCase;\nuse Auth0\\SDK\\Token;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Support\\Facades\\Event;\nuse Illuminate\\Support\\Facades\\Artisan;\nuse Illuminate\\Support\\Facades\\Cache;\n\n/*\n|--------------------------------------------------------------------------\n| Test Case\n|--------------------------------------------------------------------------\n|\n| The closure you provide to your test functions is always bound to a specific PHPUnit test\n| case class. By default, that class is \"PHPUnit\\Framework\\TestCase\". Of course, you may\n| need to change it using the \"uses()\" function to bind a different classes or traits.\n|\n*/\n\ndefine('AUTH0_LARAVEL_RUNNING_TESTS', 1);\n\nuses(TestCase::class)->in(__DIR__);\n\n// uses()->beforeAll(function (): void {\n\n// })->in(__DIR__);\n\nuses()->beforeEach(function (): void {\n    $this->events = [];\n\n    Event::listen('*', function ($event) {\n        $this->events[] = $event;\n    });\n\n    Cache::flush();\n\n    config()->set('auth', [\n        'defaults' => [\n            'guard' => 'legacyGuard',\n            'passwords' => 'users',\n        ],\n        'guards' => [\n            'web' => [\n                'driver' => 'session',\n                'provider' => 'users',\n            ],\n            'legacyGuard' => [\n                'driver' => 'auth0.guard',\n                'configuration' => 'web',\n                'provider' => 'auth0-provider',\n            ],\n            'auth0-session' => [\n                'driver' => 'auth0.authenticator',\n                'configuration' => 'web',\n                'provider' => 'auth0-provider',\n            ],\n            'auth0-api' => [\n                'driver' => 'auth0.authorizer',\n                'configuration' => 'api',\n                'provider' => 'auth0-provider',\n            ],\n        ],\n        'providers' => [\n            'users' => [\n                'driver' => 'eloquent',\n                'model' => App\\Models\\User::class,\n            ],\n            'auth0-provider' => [\n                'driver' => 'auth0.provider',\n                'repository' => 'auth0.repository',\n            ],\n        ],\n    ]);\n})->in(__DIR__);\n\n// uses()->afterEach(function (): void {\n//     $commands = ['optimize:clear'];\n\n//     foreach ($commands as $command) {\n//         Artisan::call($command);\n//     }\n// })->in(__DIR__);\n\nuses()->compact();\n\n/*\n|--------------------------------------------------------------------------\n| Expectations\n|--------------------------------------------------------------------------\n|\n| When you're writing tests, you often need to check that values meet certain conditions. The\n| \"expect()\" function gives you access to a set of \"expectations\" methods that you can use\n| to assert different things. Of course, you may extend the Expectation API at any time.\n|\n*/\n\n// expect()->extend('toBeOne', function () {\n//     return $this->toBe(1);\n// });\n\n/*\n|--------------------------------------------------------------------------\n| Functions\n|--------------------------------------------------------------------------\n|\n| While Pest is very powerful out-of-the-box, you may have some testing code specific to your\n| project that you don't want to repeat in every file. Here you can also expose helpers as\n| global functions to help you to reduce the number of lines of code in your test files.\n|\n*/\n\n// function something()\n// {\n//     // ..\n// }\n\nfunction mockIdToken(\n    string $algorithm = Token::ALGO_RS256,\n    array $claims = [],\n    array $headers = []\n): string {\n    $secret = createRsaKeys()->private;\n\n    $claims = array_merge([\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        'sub' => 'hello|world',\n        'aud' => config('auth0.guards.default.clientId'),\n        'exp' => time() + 60,\n        'iat' => time(),\n        'email' => 'john.doe@somewhere.test'\n    ], $claims);\n\n    return (string) Generator::create($secret, $algorithm, $claims, $headers);\n}\n\nfunction mockAccessToken(\n    string $algorithm = Token::ALGO_RS256,\n    array $claims = [],\n    array $headers = []\n): string {\n    $secret = createRsaKeys()->private;\n\n    $claims = array_merge([\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        'sub' => 'hello|world',\n        'aud' => config('auth0.guards.default.clientId'),\n        'iat' => time(),\n        'exp' => time() + 60,\n        'azp' => config('auth0.guards.default.clientId'),\n        'scope' => 'openid profile email',\n    ], $claims);\n\n    return (string) Generator::create($secret, $algorithm, $claims, $headers);\n}\n\nfunction createRsaKeys(\n    string $digestAlg = 'sha256',\n    int $keyType = OPENSSL_KEYTYPE_RSA,\n    int $bitLength = 2048\n): object\n{\n    $config = [\n        'digest_alg' => $digestAlg,\n        'private_key_type' => $keyType,\n        'private_key_bits' => $bitLength,\n    ];\n\n    $privateKeyResource = openssl_pkey_new($config);\n\n    if ($privateKeyResource === false) {\n        throw new RuntimeException(\"OpenSSL reported an error: \" . getSslError());\n    }\n\n    $export = openssl_pkey_export($privateKeyResource, $privateKey);\n\n    if ($export === false) {\n        throw new RuntimeException(\"OpenSSL reported an error: \" . getSslError());\n    }\n\n    $publicKey = openssl_pkey_get_details($privateKeyResource);\n\n    $resCsr = openssl_csr_new([], $privateKeyResource);\n    $resCert = openssl_csr_sign($resCsr, null, $privateKeyResource, 30);\n    openssl_x509_export($resCert, $x509);\n\n    return (object) [\n        'private' => $privateKey,\n        'public' => $publicKey['key'],\n        'cert' => $x509,\n        'resource' => $privateKeyResource,\n    ];\n}\n\nfunction getSslError(): string\n{\n    $errors = [];\n\n    while ($error = openssl_error_string()) {\n        $errors[] = $error;\n    }\n\n    return implode(', ', $errors);\n}\n"
  },
  {
    "path": "tests/TestCase.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Auth0\\Laravel\\Tests;\n\nuse Illuminate\\Foundation\\Testing\\TestCase as BaseTestCase;\nuse Auth0\\Laravel\\ServiceProvider;\nuse Orchestra\\Testbench\\Concerns\\CreatesApplication;\nuse Spatie\\LaravelRay\\RayServiceProvider;\n\nclass TestCase extends BaseTestCase\n{\n    use CreatesApplication;\n\n    protected $enablesPackageDiscoveries = true;\n    protected $events = [];\n\n    protected function getPackageProviders($app)\n    {\n        return [\n            RayServiceProvider::class,\n            ServiceProvider::class,\n        ];\n    }\n\n    protected function getEnvironmentSetUp($app): void\n    {\n        // Set a random key for testing\n        $_ENV['APP_KEY'] = 'base64:' . base64_encode(random_bytes(32));\n\n        // Setup database for testing (currently unused)\n        $app['config']->set('database.default', 'testbench');\n        $app['config']->set('database.connections.testbench', [\n            'driver' => 'sqlite',\n            'database' => ':memory:',\n            'prefix' => '',\n        ]);\n    }\n\n    /**\n     * Asserts that an event was dispatched. Optionally assert the number of times it was dispatched and/or that it was dispatched after another event.\n     *\n     * @param string $expectedEvent The event to assert was dispatched.\n     * @param int $times The number of times the event was expected to be dispatched.\n     * @param string|null $followingEvent The event that was expected to be dispatched before this event.\n     */\n    protected function assertDispatched(string $expectedEvent, int $times = 0, ?string $followingEvent = null)\n    {\n        expect($this->events)\n            ->toBeArray()\n            ->toContain($expectedEvent);\n\n        if ($times > 0) {\n            expect(array_count_values($this->events)[$expectedEvent])\n                ->toBeInt()\n                ->toBe($times);\n        }\n\n        if (null !== $followingEvent) {\n            expect($this->events)\n                ->toContain($followingEvent);\n\n            $indexExpected = array_search($expectedEvent, $this->events);\n            $indexFollowing = array_search($followingEvent, $this->events);\n\n            if ($indexExpected !== false && $indexFollowing !== false) {\n                expect($indexExpected)\n                    ->toBeInt()\n                    ->toBeGreaterThan($indexFollowing);\n            }\n        }\n    }\n\n    /**\n     * Asserts that events were dispatched in the order provided. Events not in the array are ignored.\n     *\n     * @param array<string> $events Array of events to assert were dispatched in order.\n     */\n    protected function assertDispatchedOrdered(array $events)\n    {\n        $previousIndex = -1;\n\n        foreach ($events as $event) {\n            $index = array_search($event, $this->events);\n\n            expect($index)\n                ->toBeInt()\n                ->toBeGreaterThan($previousIndex);\n\n            $previousIndex = $index;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Unit/Auth/GuardStatefulTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\Entities\\CredentialEntity;\nuse Auth0\\Laravel\\Users\\StatefulUser;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\nuse PsrMock\\Psr18\\Client as MockHttpClient;\nuse PsrMock\\Psr17\\RequestFactory as MockRequestFactory;\nuse PsrMock\\Psr17\\ResponseFactory as MockResponseFactory;\nuse PsrMock\\Psr17\\StreamFactory as MockStreamFactory;\n\nuse function Pest\\Laravel\\getJson;\n\nuses()->group('auth', 'auth.guard', 'auth.guard.stateful');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = $guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n    $this->config = $this->sdk->configuration();\n    $this->session = $this->config->getSessionStorage();\n\n    $this->user = new StatefulUser(['sub' => uniqid('auth0|')]);\n\n    $this->session->set('user', ['sub' => 'hello|world']);\n    $this->session->set('idToken', (string) Generator::create((createRsaKeys())->private));\n    $this->session->set('accessToken', (string) Generator::create((createRsaKeys())->private));\n    $this->session->set('accessTokenScope', [uniqid()]);\n    $this->session->set('accessTokenExpiration', time() + 60);\n\n    $this->route = '/' . uniqid();\n    $guard = $this->guard;\n\n    Route::get($this->route, function () use ($guard) {\n        $credential = $guard->find(Guard::SOURCE_SESSION);\n\n        if (null !== $credential) {\n            $guard->login($credential, Guard::SOURCE_SESSION);\n            return response()->json(['status' => 'OK']);\n        }\n\n        abort(Response::HTTP_UNAUTHORIZED, 'Unauthorized');\n    });\n});\n\nit('gets a user from a valid session', function (): void {\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe('hello|world');\n});\n\nit('updates internal and session states as appropriate', function (): void {\n    // Session should be available and populated\n    expect($this->session)\n        ->getAll()->not()->toBe([]);\n\n    // Guard should pick up on the session during the HTTP request\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    // Guard should have it's state populated\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe('hello|world');\n\n    // Empty guard state\n    $this->guard->logout();\n\n    // Guard should have had it's state emptied\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    // Session should have been emptied\n    expect($this->session)\n        ->getAll()->toBe([]);\n\n    // HTTP request should fail without a session.\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_UNAUTHORIZED);\n\n    // Inject a new session into the store\n    $this->session->set('user', ['sub' => 'hello|world|two']);\n\n    // Session should be available and populated again\n    expect($this->session)\n        ->getAll()->not()->toBe([]);\n\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    // Guard should pick up on the session\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe('hello|world|two');\n\n    // Directly wipe the Laravel session, circumventing the Guard APIs\n    $this->session->purge();\n\n    // Session should be empty\n    expect($this->session)\n        ->getAll()->toBe([]);\n\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_UNAUTHORIZED);\n\n    // Guard should have it's state emptied\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    $this->session->set('user', ['sub' => 'hello|world|4']);\n\n    // Session should be available\n    expect($this->session)\n        ->getAll()->not()->toBe([]);\n\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    // Guard should pick up on the session\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe('hello|world|4');\n\n    $identifier = uniqid('auth0|');\n    $user = new StatefulUser(['sub' => $identifier]);\n\n    // Overwrite state using the Guard's login()\n    $this->guard->login(CredentialEntity::create(\n        user: $user\n    ), Guard::SOURCE_SESSION);\n\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    // Guard should have it's state updated\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe($identifier);\n\n    // Session should be updated\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier]);\n});\n\nit('creates a session from login()', function (): void {\n    $identifier = uniqid('auth0|');\n    // $idToken = uniqid('id-token-');\n    // $accessToken = uniqid('access-token-');\n    $accessTokenScope = [uniqid('access-token-scope-')];\n    $accessTokenExpiration = time() + 60;\n\n    $this->session->set('user', ['sub' => $identifier]);\n    // $this->session->set('idToken', $idToken);\n    // $this->session->set('accessToken', $accessToken);\n    $this->session->set('accessTokenScope', $accessTokenScope);\n    $this->session->set('accessTokenExpiration', $accessTokenExpiration);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n\n    expect($found)\n        ->toBeInstanceOf(CredentialEntity::class);\n\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier])\n        // ->get('idToken')->toBe($idToken)\n        // ->get('accessToken')->toBe($accessToken)\n        ->get('accessTokenScope')->toBe($accessTokenScope)\n        ->get('accessTokenExpiration')->toBe($accessTokenExpiration)\n        ->get('refreshToken')->toBeNull();\n\n    $user = new StatefulUser(['sub' => $identifier]);\n\n    $changedIdToken = uniqid('CHANGED-id-token-');\n    $changedRefreshToken = uniqid('CHANGED-refresh-token-');\n\n    // Overwrite state using the Guard's login()\n    $this->guard->login(CredentialEntity::create(\n        user: $user,\n        idToken: $changedIdToken,\n        refreshToken: $changedRefreshToken\n    ), Guard::SOURCE_SESSION);\n\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe($identifier);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier])\n        ->get('idToken')->toBe($changedIdToken)\n        // ->get('accessToken')->toBe($accessToken)\n        ->get('accessTokenScope')->toBe($accessTokenScope)\n        ->get('accessTokenExpiration')->toBe($accessTokenExpiration)\n        ->get('refreshToken')->toBe($changedRefreshToken);\n});\n\nit('queries the /userinfo endpoint for refreshUser()', function (): void {\n    $identifier = uniqid('auth0|');\n    // $idToken = uniqid('id-token-');\n    // $accessToken = uniqid('access-token-');\n    $accessTokenScope = [uniqid('access-token-scope-')];\n    $accessTokenExpiration = time() + 60;\n\n    $this->session->set('user', ['sub' => $identifier]);\n    // $this->session->set('idToken', $idToken);\n    // $this->session->set('accessToken', $accessToken);\n    $this->session->set('accessTokenScope', $accessTokenScope);\n    $this->session->set('accessTokenExpiration', $accessTokenExpiration);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier]);\n\n    $response = (new MockResponseFactory)->createResponse();\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new MockStreamFactory)->createStream(\n                json_encode(\n                    value: [\n                        'sub' => $identifier,\n                        'name' => 'John Doe',\n                        'email' => '...',\n                    ],\n                    flags: JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR\n                )\n            )\n        )\n    );\n\n    $this->guard->refreshUser();\n\n    $userAttributes = $this->guard->user()->getAttributes();\n\n    expect($userAttributes)\n        ->toBeArray()\n        ->toMatchArray([\n            'sub' => $identifier,\n            'name' => 'John Doe',\n            'email' => '...',\n        ]);\n});\n\nit('does not query the /userinfo endpoint for refreshUser() if an access token is not available', function (): void {\n    $identifier = uniqid('auth0|');\n    // $idToken = uniqid('id-token-');\n    $accessTokenScope = [uniqid('access-token-scope-')];\n    $accessTokenExpiration = time() + 60;\n\n    $this->session->set('user', ['sub' => $identifier]);\n    // $this->session->set('idToken', $idToken);\n    $this->session->set('accessToken', null);\n    $this->session->set('accessTokenScope', $accessTokenScope);\n    $this->session->set('accessTokenExpiration', $accessTokenExpiration);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier]);\n\n    $requestFactory = new MockRequestFactory;\n    $responseFactory = new MockResponseFactory;\n    $streamFactory = new MockStreamFactory;\n\n    $response = $responseFactory->createResponse(200);\n    $response->getBody()->write(json_encode(\n        [\n            'sub' => $identifier,\n            'name' => 'John Doe',\n            'email' => '...',\n        ],\n        JSON_PRETTY_PRINT\n    ));\n\n    $http = new MockHttpClient(fallbackResponse: $response, requestLimit: 0);\n    $http->addResponseWildcard($response);\n\n    $this->config->setHttpRequestFactory($requestFactory);\n    $this->config->setHttpResponseFactory($responseFactory);\n    $this->config->setHttpStreamFactory($streamFactory);\n    $this->config->setHttpClient($http);\n\n    $this->guard->refreshUser();\n\n    $userAttributes = $this->guard->user()->getAttributes();\n\n    expect($userAttributes)\n        ->toBeArray()\n        ->toMatchArray([\n            'sub' => $identifier,\n        ]);\n});\n\nit('rejects bad responses from the /userinfo endpoint for refreshUser()', function (): void {\n    $identifier = uniqid('auth0|');\n    // $idToken = uniqid('id-token-');\n    // $accessToken = uniqid('access-token-');\n    $accessTokenScope = [uniqid('access-token-scope-')];\n    $accessTokenExpiration = time() + 60;\n\n    $this->session->set('user', ['sub' => $identifier]);\n    // $this->session->set('idToken', $idToken);\n    // $this->session->set('accessToken', $accessToken);\n    $this->session->set('accessTokenScope', $accessTokenScope);\n    $this->session->set('accessTokenExpiration', $accessTokenExpiration);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier]);\n\n    $response = (new MockResponseFactory)->createResponse();\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new MockStreamFactory)->createStream(\n                json_encode(\n                    value: 'bad response',\n                    flags: JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR\n                )\n            )\n        )\n    );\n\n    $this->guard->refreshUser();\n\n    $userAttributes = $this->guard->user()->getAttributes();\n\n    expect($userAttributes)\n        ->toBeArray()\n        ->toMatchArray([\n            'sub' => $identifier,\n        ]);\n});\n\nit('immediately invalidates an expired session when a refresh token is not available', function (): void {\n    $this->session->set('accessTokenExpiration', time() - 1000);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    expect($this->session)\n        ->get('user')->toBeNull();\n});\n\nit('invalidates an expired session when an access token fails to refresh', function (): void {\n    $this->session->set('accessTokenExpiration', time() - 1000);\n    $this->session->set('refreshToken', uniqid());\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    expect($this->session)\n        ->get('user')->toBeNull();\n});\n\nit('successfully continues a session when an access token is successfully refreshed', function (): void {\n    $this->session->set('accessTokenExpiration', time() - 1000);\n    $this->session->set('refreshToken', (string) Generator::create((createRsaKeys())->private));\n\n    $response = (new MockResponseFactory)->createResponse();\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new MockStreamFactory)->createStream(\n                json_encode(\n                    value: [\n                        'access_token' => (string) Generator::create((createRsaKeys())->private),\n                        'expires_in' => 60,\n                        'scope' => 'openid profile',\n                        'token_type' => 'Bearer',\n                    ],\n                    flags: JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR\n                )\n            )\n        )\n    );\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->guard)\n        ->user()->not()->toBeNull();\n\n    expect($this->session)\n        ->get('user')->not()->toBeNull();\n});\n"
  },
  {
    "path": "tests/Unit/Auth/GuardStatelessTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuse function Pest\\Laravel\\getJson;\n\nuses()->group('auth', 'auth.guard', 'auth.guard.stateless');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.audience' => ['https://example.com/health-api'],\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n    $this->config = $this->sdk->configuration();\n\n    $this->token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        \"sub\" => \"auth0|123456\",\n        \"aud\" => [\n            config('auth0.guards.default.audience')[0],\n            \"https://my-domain.auth0.com/userinfo\"\n        ],\n        \"azp\" => config('auth0.guards.default.clientId'),\n        \"exp\" => time() + 60,\n        \"iat\" => time(),\n        \"scope\" => \"openid profile read:patients read:admin\"\n    ]);\n    $this->bearerToken = ['Authorization' => 'Bearer ' . $this->token->toString()];\n\n    $this->route = '/' . uniqid();\n    $guard = $this->guard;\n\n    Route::get($this->route, function () use ($guard) {\n        $credential = $guard->find(Guard::SOURCE_TOKEN);\n\n        if (null !== $credential) {\n            $guard->login($credential, Guard::SOURCE_TOKEN);\n            return response()->json(['status' => 'OK']);\n        }\n\n        abort(Response::HTTP_UNAUTHORIZED, 'Unauthorized');\n    });\n});\n\nit('assigns a user from a good token', function (): void {\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    getJson($this->route, $this->bearerToken)\n        ->assertStatus(Response::HTTP_OK);\n\n    expect($this->guard)\n        ->user()->not()->toBeNull();\n});\n\nit('does not assign a user from a empty token', function (): void {\n    getJson($this->route, ['Authorization' => 'Bearer '])\n        ->assertStatus(Response::HTTP_UNAUTHORIZED);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n\nit('does not get a user from a bad token', function (): void {\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->setAudience(['BAD_AUDIENCE']);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    getJson($this->route, $this->bearerToken)\n        ->assertStatus(Response::HTTP_UNAUTHORIZED);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n"
  },
  {
    "path": "tests/Unit/Auth/GuardTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\Exceptions\\AuthenticationException;\nuse Auth0\\Laravel\\Entities\\CredentialEntity;\nuse Auth0\\Laravel\\Users\\StatefulUser;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Contract\\API\\ManagementInterface;\nuse Auth0\\SDK\\Exception\\ConfigurationException;\nuse Auth0\\SDK\\Token;\nuse Illuminate\\Support\\Facades\\Route;\nuse PsrMock\\Psr18\\Client as MockHttpClient;\nuse PsrMock\\Psr17\\RequestFactory as MockRequestFactory;\nuse PsrMock\\Psr17\\ResponseFactory as MockResponseFactory;\nuse PsrMock\\Psr17\\StreamFactory as MockStreamFactory;\n\nuses()->group('auth', 'auth.guard', 'auth.guard.shared');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n    $this->config = $this->sdk->configuration();\n    $this->session = $this->config->getSessionStorage();\n\n    $this->user = new StatefulUser(['sub' => uniqid('auth0|')]);\n\n    Route::middleware('auth:auth0')->get('/test', function () {\n        return 'OK';\n    });\n});\n\nit('returns its configured name', function (): void {\n    expect($this->guard)\n        ->toBeInstanceOf(Guard::class)\n        ->getName()->toBe('legacyGuard');\n});\n\nit('assigns a user at login', function (): void {\n    expect($this->guard)\n        ->toBeInstanceOf(Guard::class)\n        ->user()->toBeNull();\n\n    $this->guard->login(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    expect($this->guard)\n        ->user()->toBe($this->user);\n\n    expect($this->guard)\n        ->id()->toBe($this->user->getAuthIdentifier());\n});\n\nit('logs out a user', function (): void {\n    expect($this->guard)\n        ->toBeInstanceOf(Guard::class)\n        ->user()->toBeNull();\n\n    $this->guard->login(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    expect($this->guard)\n        ->user()->toBe($this->user);\n\n    $this->guard->logout();\n\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    expect($this->guard)\n        ->id()->toBeNull();\n});\n\nit('forgets a user', function (): void {\n    expect($this->guard)\n        ->toBeInstanceOf(Guard::class)\n        ->user()->toBeNull();\n\n    $this->guard->login(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    expect($this->guard)\n        ->user()->toBe($this->user);\n\n    $this->guard->forgetUser();\n\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    expect($this->guard)\n        ->id()->toBeNull();\n});\n\nit('checks if a user is logged in', function (): void {\n    expect($this->guard)\n        ->check()->toBeFalse();\n\n    $this->guard->login(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    expect($this->guard)\n        ->check()->toBeTrue();\n});\n\nit('checks if a user is a guest', function (): void {\n    expect($this->guard)\n        ->guest()->toBeTrue();\n\n    $this->guard->login(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    expect($this->guard)\n        ->guest()->toBeFalse();\n});\n\nit('gets the user identifier', function (): void {\n    $this->guard->login(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    expect($this->guard)\n        ->id()->toBe($this->user->getAuthIdentifier());\n});\n\nit('validates a user', function (): void {\n    $this->guard->login(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    expect($this->guard)\n        ->validate(['id' => '123'])->toBeFalse()\n        ->validate(['id' => '456'])->toBeFalse();\n});\n\nit('gets/sets a user', function (): void {\n    $this->guard->setUser($this->user);\n\n    expect($this->guard)\n        ->user()->toBe($this->user);\n});\n\nit('has a user', function (): void {\n    $this->guard->setUser($this->user);\n\n    expect($this->guard)\n        ->hasUser()->toBeTrue();\n\n    $this->guard->logout();\n\n    expect($this->guard)\n        ->hasUser()->toBeFalse();\n});\n\nit('clears an imposter at logout', function (): void {\n    $this->guard->setImpersonating(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    expect($this->guard)\n        ->hasUser()->toBeTrue()\n        ->isImpersonating()->toBeTrue();\n\n    $this->guard->logout();\n\n    expect($this->guard)\n        ->isImpersonating()->toBeFalse()\n        ->hasUser()->toBeFalse();\n});\n\nit('has a scope', function (): void {\n    $this->user = new StatefulUser(['sub' => uniqid('auth0|'), 'scope' => 'read:users 456']);\n\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        accessTokenScope: ['read:users', '456']\n    );\n\n    expect($this->guard)\n        ->hasScope('read:users', $credential)->toBeTrue()\n        ->hasScope('123', $credential)->toBeFalse()\n        ->hasScope('456', $credential)->toBeTrue()\n        ->hasScope('789', $credential)->toBeFalse()\n        ->hasScope('*', $credential)->toBeTrue();\n\n    $credential = CredentialEntity::create(\n        user: $this->user\n    );\n\n    expect($this->guard)\n        ->hasScope('read:users', $credential)->toBeFalse()\n        ->hasScope('*', $credential)->toBeTrue();\n});\n\nit('checks if a user was authenticated via remember', function (): void {\n    $this->guard->login(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    expect($this->guard)\n        ->viaRemember()->toBeFalse();\n});\n\nit('returns null if authenticate() is called without being authenticated', function (): void {\n    $response = $this->guard->authenticate();\n    expect($response)->toBeNull();\n})->throws(AuthenticationException::class, AuthenticationException::UNAUTHENTICATED);\n\nit('returns a user from authenticate() if called while authenticated', function (): void {\n    $this->guard->login(CredentialEntity::create(\n        user: $this->user\n    ));\n\n    $response = $this->guard->authenticate();\n\n    expect($response)\n        ->toBe($this->user);\n});\n\nit('gets/sets a credentials', function (): void {\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->guard->setCredential($credential);\n\n    expect($this->guard)\n        ->user()->toBe($this->user);\n});\n\nit('queries the /userinfo endpoint', function (): void {\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->guard->setCredential($credential, Guard::SOURCE_TOKEN);\n\n    expect($this->guard)\n        ->user()->toBe($this->user);\n\n    $identifier = 'updated|' . uniqid();\n\n    $response = (new MockResponseFactory)->createResponse();\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new MockStreamFactory)->createStream(\n                json_encode(\n                    value: [\n                        'sub' => $identifier,\n                        'name' => 'John Doe',\n                        'email' => '...',\n                    ],\n                    flags: JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR\n                )\n            )\n        )\n    );\n\n    $userAttributes = $this->guard->getRefreshedUser()->getAttributes();\n\n    expect($userAttributes)\n        ->toBeArray()\n        ->toMatchArray([\n            'sub' => $identifier,\n        ]);\n});\n\ntest('hasPermission(*) returns true for wildcard', function (): void {\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->guard->setCredential($credential, Guard::SOURCE_TOKEN);\n\n    expect($this->guard->hasPermission('*'))\n        ->toBeTrue();\n});\n\ntest('hasPermission() returns true for matches', function (): void {\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenDecoded: [\n            'permissions' => [\n                'read:posts',\n                'read:messages',\n                'read:users',\n            ],\n        ],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->guard->setCredential($credential, Guard::SOURCE_TOKEN);\n\n    expect($this->guard->hasPermission('read:messages'))\n        ->toBeTrue();\n\n    expect($this->guard->hasPermission('write:posts'))\n        ->toBeFalse();\n});\n\ntest('hasPermission() returns false when there are no permissions', function (): void {\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenDecoded: [\n            'permissions' => [],\n        ],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->guard->setCredential($credential, Guard::SOURCE_TOKEN);\n\n    expect($this->guard->hasPermission('read:messages'))\n        ->toBeFalse();\n});\n\ntest('management() returns a Management API class', function (): void {\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenDecoded: [\n            'permissions' => [],\n        ],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->guard->setCredential($credential, Guard::SOURCE_TOKEN);\n\n    expect($this->guard->management())\n        ->toBeInstanceOf(ManagementInterface::class);\n});\n\ntest('sdk() uses the guard name to optionally merge configuration data', function (): void {\n    config([\n        'auth0.guards.default.domain' => 'https://default-domain.com',\n        'auth0.guards.web.strategy' => 'none',\n        'auth0.guards.web.domain' => 'https://legacy-domain.com',\n    ]);\n\n    expect($this->guard->sdk()->configuration()->getDomain())\n        ->toBe('legacy-domain.com');\n});\n\ntest('sdk() configuration v1 is supported', function (): void {\n    config(['auth0' => [\n        'strategy' => 'none',\n        'domain' => 'https://v1-domain.com',\n    ]]);\n\n    expect($this->guard->sdk()->configuration()->getDomain())\n        ->toBe('v1-domain.com');\n});\n\ntest('sdk() configuration v1 defaults to an empty array', function (): void {\n    config(['auth0' => 123]);\n    $this->guard->sdk()->configuration()->getDomain();\n})->throws(ConfigurationException::class);\n"
  },
  {
    "path": "tests/Unit/Bridges/CacheBridgeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Bridges\\CacheBridge;\nuse Auth0\\Laravel\\Bridges\\CacheItemBridge;\nuse Psr\\Cache\\CacheItemInterface;\n\nuses()->group('cache', 'cache.laravel', 'cache.laravel.pool');\n\ntest('getItem(), hasItem() and save() behave as expected', function (): void {\n    $pool = new CacheBridge();\n    $cache = $pool->getItem('testing');\n\n    expect($pool)\n        ->hasItem('testing')->toBeFalse();\n\n    expect($cache)\n        ->toBeInstanceOf(CacheItemBridge::class)\n        ->get()->toBeNull()\n        ->isHit()->toBeFalse();\n\n    $cache->set(42);\n\n    expect($cache)\n        ->get()->toBeNull();\n\n    expect($pool)\n        ->save($cache)->toBeTrue();\n\n    expect($pool)\n        ->hasItem('testing')->toBeTrue();\n\n    $cache = $pool->getItem('testing');\n\n    expect($cache)\n        ->toBeInstanceOf(CacheItemBridge::class)\n        ->isHit()->toBeTrue()\n        ->get()->toEqual(42);\n\n    $results = $pool->getItems();\n\n    expect($results)\n        ->toBeArray()\n        ->toHaveCount(0);\n\n    $results = $pool->getItems(['testing']);\n\n    expect($results['testing'])\n        ->toBeInstanceOf(CacheItemBridge::class)\n        ->isHit()->toBeTrue()\n        ->get()->toEqual(42);\n\n    $this->app[\\Illuminate\\Cache\\CacheManager::class]\n        ->getStore()\n        ->put('testing', false, 60);\n\n    $cache = $pool->getItem('testing');\n\n    expect($pool)\n        ->hasItem('testing')->toBeFalse();\n\n    expect($cache)\n        ->toBeInstanceOf(CacheItemBridge::class)\n        ->get()->toBeNull()\n        ->isHit()->toBeFalse();\n\n    $cacheMock = Mockery::mock(CacheItemInterface::class);\n\n    expect($pool)\n        ->save($cacheMock)->toBeFalse();\n});\n\ntest('save() with a negative expiration value is deleted', function (): void {\n    $pool = new CacheBridge();\n    $cache = new CacheItemBridge('testing', 42, true, new DateTime('now - 1 year'));\n\n    expect($pool)->hasItem('testing')->toBeFalse();\n\n    $pool->save($cache);\n\n    expect($pool)->hasItem('testing')->toBeFalse();\n});\n\ntest('saveDeferred() behaves as expected', function (): void {\n    $pool = new CacheBridge();\n    $cache = new CacheItemBridge('testing', 42, true, new DateTime('now + 1 hour'));\n\n    expect($pool)\n        ->hasItem('testing')->toBeFalse()\n        ->saveDeferred($cache)->toBeTrue()\n        ->hasItem('testing')->toBeFalse()\n        ->commit()->toBeTrue()\n        ->hasItem('testing')->toBeTrue();\n});\n\ntest('save() with a false value is discarded', function (): void {\n    $pool = new CacheBridge();\n    $cache = new CacheItemBridge('testing', false, true, new DateTime('now + 1 hour'));\n\n    expect($pool)\n        ->hasItem('testing')->toBeFalse()\n        ->save($cache)->toBeTrue()\n        ->hasItem('testing')->toBeFalse();\n});\n\ntest('saveDeferred() returns false when the wrong type of interface is saved', function (): void {\n    $pool = new CacheBridge();\n    $cache = new CacheItemBridge('testing', 42, true, new DateTime('now + 1 hour'));\n\n    $cache = new class implements CacheItemInterface {\n        public function getKey(): string\n        {\n            return 'testing';\n        }\n\n        public function get(): mixed\n        {\n            return 42;\n        }\n\n        public function isHit(): bool\n        {\n            return true;\n        }\n\n        public function set(mixed $value): static\n        {\n            return $this;\n        }\n\n        public function expiresAt($expiration): static\n        {\n            return $this;\n        }\n\n        public function expiresAfter($time): static\n        {\n            return $this;\n        }\n    };\n\n    expect($pool)\n        ->hasItem('testing')->toBeFalse()\n        ->saveDeferred($cache)->toBeFalse()\n        ->hasItem('testing')->toBeFalse();\n});\n\ntest('deleteItem() behaves as expected', function (): void {\n    $pool = new CacheBridge();\n    $cache = new CacheItemBridge('testing', 42, true, new DateTime('now + 1 minute'));\n\n    expect($pool)->hasItem('testing')->toBeFalse();\n\n    $pool->save($cache);\n\n    expect($pool)->hasItem('testing')->toBeTrue();\n\n    $cache = $pool->getItem('testing');\n\n    expect($cache)\n        ->isHit()->toBeTrue()\n        ->get()->toBe(42);\n\n    $pool->deleteItem('testing');\n\n    expect($pool)\n        ->hasItem('testing')->toBeFalse();\n\n    $cache = $pool->getItem('testing');\n\n    expect($cache)\n        ->isHit()->toBeFalse()\n        ->get()->toBeNull();\n});\n\ntest('deleteItems() behaves as expected', function (): void {\n    $pool = new CacheBridge();\n\n    expect($pool)\n        ->hasItem('testing1')->toBeFalse()\n        ->hasItem('testing2')->toBeFalse()\n        ->hasItem('testing3')->toBeFalse();\n\n    $cache = new CacheItemBridge('testing1', uniqid(), true, new DateTime('now + 1 minute'));\n    $pool->save($cache);\n\n    $cache = new CacheItemBridge('testing2', uniqid(), true, new DateTime('now + 1 minute'));\n    $pool->save($cache);\n\n    $cache = new CacheItemBridge('testing3', uniqid(), true, new DateTime('now + 1 minute'));\n    $pool->save($cache);\n\n    expect($pool)\n        ->hasItem('testing1')->toBeTrue()\n        ->hasItem('testing2')->toBeTrue()\n        ->hasItem('testing3')->toBeTrue();\n\n    $results = $pool->getItems(['testing1' => 1, 'testing2' => 2, 'testing3' => 3]);\n\n    expect($results)\n        ->toHaveKey('testing1')\n        ->toHaveKey('testing2')\n        ->toHaveKey('testing3')\n        ->testing1->isHit()->toBeTrue()\n        ->testing2->isHit()->toBeTrue()\n        ->testing3->isHit()->toBeTrue();\n\n    expect($pool)\n        ->deleteItems(['testing1', 'testing2', 'testing3'])->toBeTrue()\n        ->hasItem('testing1')->toBeFalse()\n        ->hasItem('testing2')->toBeFalse()\n        ->hasItem('testing3')->toBeFalse()\n        ->deleteItems(['testing4', 'testing5', 'testing6'])->toBeFalse();\n});\n\ntest('clear() behaves as expected', function (): void {\n    $pool = new CacheBridge();\n\n    expect($pool)\n        ->hasItem('testing1')->toBeFalse()\n        ->hasItem('testing2')->toBeFalse()\n        ->hasItem('testing3')->toBeFalse();\n\n    $cache = new CacheItemBridge('testing1', uniqid(), true, new DateTime('now + 1 minute'));\n    $pool->save($cache);\n\n    $cache = new CacheItemBridge('testing2', uniqid(), true, new DateTime('now + 1 minute'));\n    $pool->save($cache);\n\n    $cache = new CacheItemBridge('testing3', uniqid(), true, new DateTime('now + 1 minute'));\n    $pool->save($cache);\n\n    expect($pool)\n        ->hasItem('testing1')->toBeTrue()\n        ->hasItem('testing2')->toBeTrue()\n        ->hasItem('testing3')->toBeTrue();\n\n    $results = $pool->getItems(['testing1' => 1, 'testing2' => 2, 'testing3' => 3]);\n\n    expect($results)\n        ->toHaveKey('testing1')\n        ->toHaveKey('testing2')\n        ->toHaveKey('testing3')\n        ->testing1->isHit()->toBeTrue()\n        ->testing2->isHit()->toBeTrue()\n        ->testing3->isHit()->toBeTrue();\n\n    $pool->clear();\n\n    expect($pool)\n        ->hasItem('testing1')->toBeFalse()\n        ->hasItem('testing2')->toBeFalse()\n        ->hasItem('testing3')->toBeFalse();\n});\n"
  },
  {
    "path": "tests/Unit/Bridges/CacheItemBridgeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Bridges\\CacheItemBridge;\n\nuses()->group('cache', 'cache.laravel', 'cache.laravel.item');\n\ntest('getKey() returns an expected value', function (): void {\n    $cacheItem = new CacheItemBridge('testing', 42, true);\n\n    expect($cacheItem->getKey())\n        ->toBe('testing');\n});\n\ntest('get() returns an expected value when hit', function (): void {\n    $cacheItem = new CacheItemBridge('testing', 42, true);\n\n    expect($cacheItem->get())\n        ->toBe(42);\n});\n\ntest('get() returns null when no hit', function (): void {\n    $cacheItem = new CacheItemBridge('testing', 42, false);\n\n    expect($cacheItem->get())\n        ->toBeNull();\n});\n\ntest('getRawValue() returns an expected value', function (): void {\n    $cacheItem = new CacheItemBridge('testing', 42, false);\n\n    expect($cacheItem->getRawValue())\n        ->toBe(42);\n});\n\ntest('isHit() returns an expected value when hit', function (): void {\n    $cacheItem = new CacheItemBridge('testing', 42, true);\n\n    expect($cacheItem->isHit())\n        ->toBeTrue();\n\n    $cacheItem = new CacheItemBridge('testing', 42, false);\n\n    expect($cacheItem->isHit())\n        ->toBeFalse();\n});\n\ntest('set() alters the stored value as expected', function (): void {\n    $cacheItem = new CacheItemBridge('testing', 42, true);\n\n    expect($cacheItem->get())\n        ->toBe(42);\n\n    expect($cacheItem->set(43))\n        ->toBe($cacheItem)\n        ->get()->toBe(43);\n});\n\ntest('expiresAt() defaults to +1 year and accepts changes to its value', function (): void {\n    $cacheItem = new CacheItemBridge('testing', 42, true);\n\n    expect($cacheItem->getExpiration()->getTimestamp())\n        ->toBeGreaterThan((new DateTime('now +1 year -1 minute'))->getTimestamp())\n        ->toBeLessThan((new DateTime('now +1 year +1 minute'))->getTimestamp());\n\n    $cacheItem->expiresAt(new DateTime('now +1 day'));\n\n    expect($cacheItem->getExpiration()->getTimestamp())\n        ->toBeGreaterThan((new DateTime('now +1 day -1 minute'))->getTimestamp())\n        ->toBeLessThan((new DateTime('now +1 day +1 minute'))->getTimestamp());\n});\n\ntest('expiresAfter() defaults to +1 year and accepts changes to its value', function (): void {\n    $cacheItem = new CacheItemBridge('testing', 42, true);\n\n    expect($cacheItem->getExpiration()->getTimestamp())\n    ->toBeGreaterThan((new DateTime('now +1 year -1 minute'))->getTimestamp())\n    ->toBeLessThan((new DateTime('now +1 year +1 minute'))->getTimestamp());\n\n    $cacheItem->expiresAfter(300);\n\n    expect($cacheItem->getExpiration()->getTimestamp())\n        ->toBeGreaterThan((new DateTime('now +250 seconds'))->getTimestamp())\n        ->toBeLessThan((new DateTime('now +350 seconds'))->getTimestamp());\n});\n\ntest('miss() returns a configured instance', function (): void {\n    $cacheItem = new CacheItemBridge('testing', 42, true);\n    $newCacheItem = $cacheItem->miss('testing123');\n\n    expect($cacheItem->getKey())\n        ->toBe('testing');\n\n    expect($newCacheItem->getKey())\n        ->toBe('testing123');\n\n    expect($newCacheItem->get())\n        ->not()->toBe($cacheItem->get());\n});\n"
  },
  {
    "path": "tests/Unit/Bridges/SessionBridgeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Bridges\\SessionBridge;\nuse Auth0\\SDK\\Contract\\StoreInterface;\n\nuses()->group('session-store');\n\nit('throws an exception when an empty prefix is provided', function (): void {\n    expect(function () {\n        new SessionBridge(\n            prefix: '',\n        );\n    })->toThrow(InvalidArgumentException::class);\n});\n\nit('accepts and uses a specified prefix', function (): void {\n    $prefix = uniqid();\n\n    $store = new SessionBridge(\n        prefix: $prefix,\n    );\n\n    expect($store)\n        ->toBeInstanceOf(StoreInterface::class)\n        ->getPrefix()->toBe($prefix);\n});\n\nit('allows updating the prefix', function (): void {\n    $store = new SessionBridge();\n\n    expect($store)\n        ->toBeInstanceOf(StoreInterface::class)\n        ->getPrefix()->toBe('auth0');\n\n    $prefix = uniqid();\n    $store->setPrefix($prefix);\n\n    expect($store)\n        ->toBeInstanceOf(StoreInterface::class)\n        ->getPrefix()->toBe($prefix);\n});\n\nit('supports CRUD operations', function (): void {\n    $prefix = uniqid();\n\n    $store = new SessionBridge(\n        prefix: $prefix,\n    );\n\n    expect($store)\n        ->toBeInstanceOf(StoreInterface::class)\n        ->get('test')->toBeNull()\n        ->set('test', 'value')->toBeNull()\n        ->get('test')->toBe('value')\n        ->getAll()->toBe(['test' => 'value'])\n        ->set('test2', 'value2')->toBeNull()\n        ->getAll()->toBe(['test' => 'value', 'test2' => 'value2'])\n        ->delete('test')->toBeNull()\n        ->getAll()->toBe(['test2' => 'value2'])\n        ->set('test3', 'value3')->toBeNull()\n        ->getAll()->toBe(['test2' => 'value2', 'test3' => 'value3'])\n        ->purge()->toBeNull()\n        ->getAll()->toBe([]);\n});\n"
  },
  {
    "path": "tests/Unit/ConfigurationTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Configuration;\n\nuses()->group('Configuration');\n\ntest('stringToArrayOrNull() behaves as expected', function (): void {\n    expect(Configuration::stringToArrayOrNull('foo bar baz'))\n        ->toBe(['foo', 'bar', 'baz']);\n\n    expect(Configuration::stringToArrayOrNull(['foo', 'bar', 'baz']))\n        ->toBe(['foo', 'bar', 'baz']);\n\n    expect(Configuration::stringToArrayOrNull('   '))\n        ->toBeNull();\n});\n\ntest('stringToArray() behaves as expected', function (): void {\n    expect(Configuration::stringToArray('foo bar baz'))\n        ->toBe(['foo', 'bar', 'baz']);\n\n    expect(Configuration::stringToArray(['foo', 'bar', 'baz']))\n        ->toBe(['foo', 'bar', 'baz']);\n\n    expect(Configuration::stringToArray('   '))\n        ->toBeArray()\n        ->toHaveCount(0);\n});\n\ntest('stringToBoolOrNull() behaves as expected', function (): void {\n    expect(Configuration::stringToBoolOrNull('true'))\n        ->toBeTrue();\n\n    expect(Configuration::stringToBoolOrNull('false'))\n        ->toBeFalse();\n\n    expect(Configuration::stringToBoolOrNull('foo'))\n        ->toBeNull();\n\n    expect(Configuration::stringToBoolOrNull('foo', true))\n        ->toBeTrue();\n\n    expect(Configuration::stringToBoolOrNull('foo', false))\n        ->toBeFalse();\n});\n\ntest('stringOrNull() behaves as expected', function (): void {\n    expect(Configuration::stringOrNull(123))\n        ->toBeNull();\n\n    expect(Configuration::stringOrNull('     456 '))\n        ->toEqual('456');\n\n    expect(Configuration::stringOrNull('      '))\n        ->toBeNull();\n\n    expect(Configuration::stringOrNull('empty'))\n        ->toBeNull();\n\n    expect(Configuration::stringOrNull('(empty)'))\n        ->toBeNull();\n\n    expect(Configuration::stringOrNull('null'))\n        ->toBeNull();\n\n    expect(Configuration::stringOrNull('(null)'))\n        ->toBeNull();\n});\n\ntest('stringOrIntToIntOrNull() behaves as expected', function (): void {\n    expect(Configuration::stringOrIntToIntOrNull(123))\n        ->toEqual(123);\n\n    expect(Configuration::stringOrIntToIntOrNull('     456 '))\n        ->toEqual(456);\n\n    expect(Configuration::stringOrIntToIntOrNull('      '))\n        ->toBeNull();\n\n    expect(Configuration::stringOrIntToIntOrNull('     abc '))\n        ->toBeNull();\n});\n\ntest('get() ignores quickstart placeholders', function (): void {\n    putenv('AUTH0_DOMAIN={DOMAIN}');\n    putenv('AUTH0_CLIENT_ID={CLIENT_ID}');\n    putenv('AUTH0_CLIENT_SECRET={CLIENT_SECRET}');\n    putenv('AUTH0_AUDIENCE={API_IDENTIFIER}');\n    putenv('AUTH0_CUSTOM_DOMAIN=https://example.com');\n\n    expect(Configuration::get(Configuration::CONFIG_CUSTOM_DOMAIN))\n        ->toBeString('https://example.com');\n\n    expect(Configuration::get(Configuration::CONFIG_DOMAIN))\n        ->toBeNull();\n\n    expect(Configuration::get(Configuration::CONFIG_CLIENT_ID))\n        ->toBeNull();\n\n    expect(Configuration::get(Configuration::CONFIG_CLIENT_SECRET))\n        ->toBeNull();\n\n    expect(Configuration::get(Configuration::CONFIG_AUDIENCE))\n        ->toBeNull();\n});\n\ntest('get() behaves as expected', function (): void {\n    config(['test' => [\n        Configuration::CONFIG_AUDIENCE => implode(',', [uniqid(), uniqid()]),\n        Configuration::CONFIG_SCOPE => [],\n        Configuration::CONFIG_ORGANIZATION => '',\n\n        Configuration::CONFIG_USE_PKCE => true,\n        Configuration::CONFIG_HTTP_TELEMETRY => 'true',\n        Configuration::CONFIG_COOKIE_SECURE => 123,\n        Configuration::CONFIG_PUSHED_AUTHORIZATION_REQUEST => false,\n\n        'tokenLeeway' => 123,\n    ]]);\n\n    define('AUTH0_OVERRIDE_CONFIGURATION', 'test');\n\n    expect(Configuration::get(Configuration::CONFIG_AUDIENCE))\n        ->toBeArray()\n        ->toHaveCount(2);\n\n    expect(Configuration::get(Configuration::CONFIG_SCOPE))\n        ->toBeNull();\n\n    expect(Configuration::get(Configuration::CONFIG_ORGANIZATION))\n        ->toBeNull();\n\n    expect(Configuration::get(Configuration::CONFIG_USE_PKCE))\n        ->toBeTrue();\n\n    expect(Configuration::get(Configuration::CONFIG_HTTP_TELEMETRY))\n        ->toBeTrue();\n\n    expect(Configuration::get(Configuration::CONFIG_COOKIE_SECURE))\n        ->toBeNull();\n\n    expect(Configuration::get(Configuration::CONFIG_PUSHED_AUTHORIZATION_REQUEST))\n        ->toBeFalse();\n\n    expect(Configuration::get('tokenLeeway'))\n        ->toBeInt()\n        ->toEqual(123);\n});\n\ntest('string() behaves as expected', function (): void {\n    config(['test2' => [\n        'testInteger' => 123,\n        'testString' => '123',\n    ]]);\n\n    define('AUTH0_OVERRIDE_CONFIGURATION_STRING_METHOD', 'test2');\n\n    expect(Configuration::string('test2.testInteger'))\n        ->toBeNull();\n\n    expect(Configuration::string('test2.testString'))\n        ->toBeString()\n        ->toEqual('123');\n});\n"
  },
  {
    "path": "tests/Unit/Controllers/CallbackControllerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Controllers\\CallbackController;\nuse Auth0\\Laravel\\Events\\AuthenticationFailed;\nuse Auth0\\Laravel\\Events\\AuthenticationSucceeded;\nuse Auth0\\Laravel\\Exceptions\\ControllerException;\nuse Auth0\\Laravel\\Exceptions\\Controllers\\CallbackControllerException;\nuse Auth0\\Laravel\\Users\\ImposterUser;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Exception\\StateException;\nuse Auth0\\SDK\\Token;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Auth\\Events\\Attempting;\nuse Illuminate\\Auth\\Events\\Authenticated;\nuse Illuminate\\Auth\\Events\\Failed;\nuse Illuminate\\Auth\\Events\\Login;\nuse Illuminate\\Auth\\Events\\Validated;\nuse Illuminate\\Http\\Response;\n\nuses()->group('stateful', 'controller', 'controller.stateful', 'controller.stateful.callback');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->guard->sdk();\n    $this->config = $this->guard->sdk()->configuration();\n    $this->user = new ImposterUser(['sub' => uniqid('auth0|')]);\n\n    Route::get('/auth0/callback', CallbackController::class)->name('callback');\n});\n\nit('redirects home if an incompatible guard is active', function (): void {\n    config([\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null,\n    ]);\n\n    expect(function () {\n        $this->withoutExceptionHandling()\n             ->getJson('/auth0/callback')\n             ->assertStatus(Response::HTTP_INTERNAL_SERVER_ERROR);\n    })->toThrow(ControllerException::class);\n});\n\nit('accepts code and state parameters', function (): void {\n    expect(function () {\n        $this->withoutExceptionHandling()\n             ->getJson('/auth0/callback?code=code&state=state');\n    })->toThrow(StateException::class);\n\n    $this->assertDispatched(Attempting::class, 1);\n    $this->assertDispatched(Failed::class, 1);\n    $this->assertDispatched(AuthenticationFailed::class, 1);\n\n    $this->assertDispatchedOrdered([\n        Attempting::class,\n        Failed::class,\n        AuthenticationFailed::class,\n    ]);\n});\n\nit('accepts error and error_description parameters', function (): void {\n    expect(function () {\n        $this->withoutExceptionHandling()\n             ->getJson('/auth0/callback?error=123&error_description=456');\n    })->toThrow(CallbackControllerException::class);\n\n    $this->assertDispatched(Attempting::class, 1);\n    $this->assertDispatched(Failed::class, 1);\n    $this->assertDispatched(AuthenticationFailed::class, 1);\n\n    $this->assertDispatchedOrdered([\n        Attempting::class,\n        Failed::class,\n        AuthenticationFailed::class,\n    ]);\n});\n\nit('returns a user and sets up a session', function (): void {\n    $this->config->setTokenAlgorithm(Token::ALGO_HS256);\n\n    $state = uniqid();\n    $pkce = uniqid();\n    $nonce = uniqid();\n    $verifier = uniqid();\n\n    $idToken = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        'sub' => 'hello|world',\n        'aud' => config('auth0.guards.default.clientId'),\n        'exp' => time() + 60,\n        'iat' => time(),\n        'email' => 'john.doe@somewhere.test',\n        'nonce' => $nonce\n    ], []);\n\n    $accessToken = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        'sub' => 'hello|world',\n        'aud' => config('auth0.guards.default.clientId'),\n        'iat' => time(),\n        'exp' => time() + 60,\n        'azp' => config('auth0.guards.default.clientId'),\n        'scope' => 'openid profile email'\n    ], []);\n\n    $factory = $this->config->getHttpResponseFactory();\n    $response = $factory->createResponse();\n    $response->getBody()->write(json_encode([\n        'access_token' => $accessToken->toString(),\n        'id_token' => $idToken->toString(),\n        'scope' => 'openid profile email',\n        'expires_in' => 60,\n    ]));\n\n    $client = $this->config->getHttpClient();\n    $client->addResponse('POST', 'https://' . config('auth0.guards.default.domain') . '/oauth/token', $response);\n\n    $this->withSession([\n        'auth0_transient' => json_encode([\n            'state' => $state,\n            'pkce' => $pkce,\n            'nonce' => $nonce,\n            'code_verifier' => $verifier\n        ])\n    ])->getJson('/auth0/callback?code=code&state=' . $state)\n        ->assertFound()\n        ->assertLocation('/');\n\n    $this->assertDispatched(Attempting::class, 1);\n    $this->assertDispatched(Validated::class, 1);\n    $this->assertDispatched(Login::class, 1);\n    $this->assertDispatched(AuthenticationSucceeded::class, 1);\n    $this->assertDispatched(Authenticated::class, 1);\n\n    $this->assertDispatchedOrdered([\n        Attempting::class,\n        Validated::class,\n        Login::class,\n        AuthenticationSucceeded::class,\n        Authenticated::class,\n    ]);\n});\n\nit('redirects visitors if an expected parameter is not provided', function (): void {\n    $this->getJson('/auth0/callback?code=code')\n         ->assertFound()\n         ->assertLocation('/login');\n});\n"
  },
  {
    "path": "tests/Unit/Controllers/LoginControllerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Controllers\\LoginController;\nuse Auth0\\Laravel\\Exceptions\\ControllerException;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Illuminate\\Support\\Facades\\Route;\nuse Auth0\\Laravel\\Http\\Controller\\Stateful\\Login;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Http\\Response;\n\nuses()->group('stateful', 'controller', 'controller.stateful', 'controller.stateful.login');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $this->validSession = [\n        'auth0_session' => json_encode([\n            'user' => ['sub' => 'hello|world'],\n            'idToken' => (string) Generator::create((createRsaKeys())->private),\n            'accessToken' => (string) Generator::create((createRsaKeys())->private),\n            'accessTokenScope' => [uniqid()],\n            'accessTokenExpiration' => time() + 60,\n        ])\n    ];\n\n    Route::get('/login', LoginController::class);\n});\n\nit('redirects to the home route if an incompatible guard is active', function (): void {\n    config($config = [\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null,\n    ]);\n\n    expect(function () {\n        $this->withoutExceptionHandling()\n             ->getJson('/login')\n             ->assertStatus(Response::HTTP_INTERNAL_SERVER_ERROR);\n    })->toThrow(ControllerException::class);\n});\n\nit('redirects to the home route when a user is already logged in', function (): void {\n    $this->withSession($this->validSession)\n         ->get('/login')\n            ->assertRedirect('/');\n});\n\nit('redirects to the Universal Login Page', function (): void {\n    $this->get('/login')\n         ->assertRedirectContains('/authorize');\n});\n"
  },
  {
    "path": "tests/Unit/Controllers/LogoutControllerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Controllers\\LogoutController;\nuse Auth0\\Laravel\\Exceptions\\ControllerException;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('stateful', 'controller', 'controller.stateful', 'controller.stateful.logout');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $this->validSession = [\n        'auth0_session' => json_encode([\n            'user' => ['sub' => 'hello|world'],\n            'idToken' => (string) Generator::create((createRsaKeys())->private),\n            'accessToken' => (string) Generator::create((createRsaKeys())->private),\n            'accessTokenScope' => [uniqid()],\n            'accessTokenExpiration' => time() + 60,\n        ])\n    ];\n\n    Route::get('/logout', LogoutController::class);\n});\n\nit('redirects to the home route if an incompatible guard is active', function (): void {\n    config($config = [\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null\n    ]);\n\n    expect(function () {\n        $this->withoutExceptionHandling()\n             ->getJson('/logout')\n             ->assertStatus(Response::HTTP_INTERNAL_SERVER_ERROR);\n    })->toThrow(ControllerException::class);\n});\n\nit('redirects to the home route when a user is not already logged in', function (): void {\n    $this->get('/logout')\n         ->assertRedirect('/');\n});\n\nit('redirects to the Auth0 logout endpoint', function (): void {\n    $this->withSession($this->validSession)\n         ->get('/logout')\n            ->assertRedirectContains('/v2/logout');\n});\n"
  },
  {
    "path": "tests/Unit/Entities/CredentialEntityTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Entities\\CredentialEntity;\nuse Auth0\\Laravel\\Users\\StatelessUser;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token;\n\nuses()->group('stateful', 'model', 'model.credential');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.audience' => [uniqid()],\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $this->user = new StatelessUser(['sub' => uniqid('auth0|')]);\n    $this->idToken = mockIdToken(algorithm: Token::ALGO_HS256);\n    $this->accessToken = mockAccessToken(algorithm: Token::ALGO_HS256);\n    $this->accessTokenScope = ['openid', 'profile', 'email', uniqid()];\n    $this->accessTokenDecoded = [uniqid(), ['hello' => 'world']];\n    $this->accessTokenExpiration = time() + 3600;\n    $this->refreshToken = uniqid();\n});\n\ntest('create() returns a properly configured instance', function (): void {\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        idToken: $this->idToken,\n        accessToken: $this->accessToken,\n        accessTokenScope: $this->accessTokenScope,\n        accessTokenExpiration: $this->accessTokenExpiration,\n        refreshToken: $this->refreshToken\n    );\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getUser()->toBe($this->user)\n        ->getIdToken()->toBe($this->idToken)\n        ->getAccessToken()->toBe($this->accessToken)\n        ->getAccessTokenScope()->toBe($this->accessTokenScope)\n        ->getAccessTokenExpiration()->toBe($this->accessTokenExpiration)\n        ->getRefreshToken()->toBe($this->refreshToken);\n});\n\nit('clear() nullifies all properties', function (): void {\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        idToken: $this->idToken,\n        accessToken: $this->accessToken,\n        accessTokenScope: $this->accessTokenScope,\n        accessTokenExpiration: $this->accessTokenExpiration,\n        refreshToken: $this->refreshToken,\n    );\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getUser()->toBe($this->user)\n        ->getIdToken()->toBe($this->idToken)\n        ->getAccessToken()->toBe($this->accessToken)\n        ->getAccessTokenScope()->toBe($this->accessTokenScope)\n        ->getAccessTokenExpiration()->toBe($this->accessTokenExpiration)\n        ->getRefreshToken()->toBe($this->refreshToken);\n\n    expect($credential->clear())\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getUser()->toBeNull()\n        ->getIdToken()->toBeNull()\n        ->getAccessToken()->toBeNull()\n        ->getAccessTokenScope()->toBeNull()\n        ->getAccessTokenExpiration()->toBeNull()\n        ->getRefreshToken()->toBeNull();\n});\n\nit('setUser() assigns a correct value', function (): void {\n    $credential = CredentialEntity::create();\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getUser()->toBeNull();\n\n    expect($credential->setUser($this->user))\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getUser()->toBe($this->user);\n});\n\nit('setIdToken() assigns a correct value', function (): void {\n    $credential = CredentialEntity::create();\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getIdToken()->toBeNull();\n\n    expect($credential->setIdToken($this->idToken))\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getIdToken()->toBe($this->idToken);\n});\n\nit('setAccessToken() assigns a correct value', function (): void {\n    $credential = CredentialEntity::create();\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessToken()->toBeNull();\n\n    expect($credential->setAccessToken($this->accessToken))\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessToken()->toBe($this->accessToken);\n});\n\nit('setAccessTokenScope() assigns a correct value', function (): void {\n    $credential = CredentialEntity::create();\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessTokenScope()->toBeNull();\n\n    expect($credential->setAccessTokenScope($this->accessTokenScope))\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessTokenScope()->toBe($this->accessTokenScope);\n});\n\nit('setAccessTokenDecoded() assigns a correct value', function (): void {\n    $credential = CredentialEntity::create();\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessTokenDecoded()->toBeNull();\n\n    expect($credential->setAccessTokenDecoded($this->accessTokenDecoded))\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessTokenDecoded()->toBe($this->accessTokenDecoded);\n});\n\nit('setAccessTokenExpiration() assigns a correct value', function (): void {\n    $credential = CredentialEntity::create();\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessTokenExpiration()->toBeNull();\n\n    expect($credential->setAccessTokenExpiration($this->accessTokenExpiration))\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessTokenExpiration()->toBe($this->accessTokenExpiration);\n});\n\nit('setRefreshToken() assigns a correct value', function (): void {\n    $credential = CredentialEntity::create();\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getRefreshToken()->toBeNull();\n\n    expect($credential->setRefreshToken($this->refreshToken))\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getRefreshToken()->toBe($this->refreshToken);\n});\n\nit('getAccessTokenExpired() returns a correct value', function (): void {\n    $credential = CredentialEntity::create();\n\n    expect($credential)\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessTokenExpired()->toBeNull();\n\n    expect($credential->setAccessTokenExpiration($this->accessTokenExpiration))\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessTokenExpired()->toBeFalse();\n\n    expect($credential->setAccessTokenExpiration($this->accessTokenExpiration - 3600 * 2))\n        ->toBeInstanceOf(CredentialEntity::class)\n        ->getAccessTokenExpired()->toBeTrue();\n});\n\nit('jsonSerialize() returns a correct structure', function (): void {\n    $credential = CredentialEntity::create(\n        user: $this->user,\n        idToken: $this->idToken,\n        accessToken: $this->accessToken,\n        accessTokenScope: $this->accessTokenScope,\n        accessTokenExpiration: $this->accessTokenExpiration,\n        refreshToken: $this->refreshToken,\n    );\n\n    expect(json_encode($credential))\n        ->json()\n            ->user->toBe(json_encode($this->user))\n            ->idToken->toBe($this->idToken)\n            ->accessToken->toBe($this->accessToken)\n            ->accessTokenScope->toBe($this->accessTokenScope)\n            ->accessTokenExpiration->toBe($this->accessTokenExpiration)\n            ->refreshToken->toBe($this->refreshToken);\n});\n"
  },
  {
    "path": "tests/Unit/Entities/InstanceEntityTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Entities\\InstanceEntity;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Exception\\ConfigurationException;\n\nuses()->group('Entities/InstanceEntity');\n\nbeforeEach(function (): void {\n});\n\nit('instantiates an empty configuration if a non-array is supplied', function (): void {\n    config(['auth0' => true]);\n\n    (new InstanceEntity())->getConfiguration();\n})->throws(ConfigurationException::class);\n\ntest('setGuardConfigurationKey() sets the guard configuration key', function (): void {\n    $key = uniqid();\n    $instance = new InstanceEntity();\n    $instance->setGuardConfigurationKey($key);\n\n    expect($instance->getGuardConfigurationKey())\n        ->toBe($key);\n});\n\ntest('setConfiguration sets the configuration using an SdkConfiguration', function (): void {\n    $instance = new InstanceEntity();\n    $configuration = new SdkConfiguration(['strategy' => 'none', 'domain' => uniqid() . '.auth0.test']);\n\n    $instance->setConfiguration($configuration);\n\n    expect($instance->getConfiguration())\n        ->toBe($configuration);\n});\n\ntest('setConfiguration sets the configuration using an array', function (): void {\n    $instance = new InstanceEntity();\n    $configuration = ['strategy' => 'none', 'domain' => uniqid() . '.auth0.test'];\n\n    $instance->setConfiguration($configuration);\n\n    expect($instance->getConfiguration())\n        ->toBeInstanceOf(SdkConfiguration::class);\n});\n\ntest('::create() sets the guard configuration key and configuration', function (): void {\n    $key = uniqid();\n    $configuration = new SdkConfiguration(['strategy' => 'none', 'domain' => uniqid() . '.auth0.test']);\n    $instance = InstanceEntity::create(\n        configuration: $configuration,\n        guardConfigurationName: $key,\n    );\n\n    expect($instance->getConfiguration())\n        ->toBe($configuration);\n\n    expect($instance->getGuardConfigurationKey())\n        ->toBe($key);\n});\n"
  },
  {
    "path": "tests/Unit/Guards/AuthenticationGuardTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\Configuration;\nuse Auth0\\Laravel\\Entities\\CredentialEntity;\nuse Auth0\\Laravel\\Users\\StatefulUser;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\nuse PsrMock\\Psr17\\ResponseFactory;\nuse PsrMock\\Psr17\\StreamFactory;\n\nuse function Pest\\Laravel\\getJson;\n\nuses()->group('auth', 'auth.guard', 'auth.guard.session');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = $guard = auth('auth0-session');\n    $this->sdk = $this->laravel->getSdk();\n    $this->config = $this->sdk->configuration();\n    $this->session = $this->config->getSessionStorage();\n\n    $this->user = new StatefulUser(['sub' => uniqid('auth0|')]);\n\n    $this->session->set('user', ['sub' => 'hello|world']);\n    $this->session->set('idToken', (string) Generator::create((createRsaKeys())->private));\n    $this->session->set('accessToken', (string) Generator::create((createRsaKeys())->private));\n    $this->session->set('accessTokenScope', [uniqid()]);\n    $this->session->set('accessTokenExpiration', time() + 60);\n\n    $this->route = '/' . uniqid();\n    $this->route2 = '/' . uniqid();\n    $guard = $this->guard;\n\n    Route::get($this->route, function () use ($guard) {\n        $credential = $guard->find(Guard::SOURCE_SESSION);\n\n        if (null !== $credential) {\n            $guard->login($credential, Guard::SOURCE_SESSION);\n            return response()->json(['status' => 'OK']);\n        }\n\n        abort(Response::HTTP_UNAUTHORIZED, 'Unauthorized');\n    });\n\n    Route::get($this->route2, function () use ($guard) {\n        return response()->json(['user' => $guard->user(Guard::SOURCE_SESSION)->getAuthIdentifier()]);\n    });\n});\n\nit('retrieves the authenticated user from a valid session using find()', function (): void {\n    $result = $this->guard->find();\n\n    expect($result)->toBeInstanceOf(CredentialEntity::class);\n    expect($result->getUser())->toBeInstanceOf(StatefulUser::class);\n\n    expect($this->guard->user()->getAuthIdentifier())->toBe('hello|world');\n});\n\nit('retrieves the authenticated user from a valid session using user()', function (): void {\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe('hello|world');\n});\n\nit('updates internal and session states as appropriate', function (): void {\n    // Session should be available and populated\n    expect($this->session)\n        ->getAll()->not()->toBe([]);\n\n    // Guard should pick up on the session during the HTTP request\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    // Guard should have it's state populated\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe('hello|world');\n\n    // Empty guard state\n    $this->guard->logout();\n\n    // Guard should have had it's state emptied\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    // Session should have been emptied\n    expect($this->session)\n        ->getAll()->toBe([]);\n\n    // HTTP request should fail without a session.\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_UNAUTHORIZED);\n\n    // Inject a new session into the store\n    $this->session->set('user', ['sub' => 'hello|world|two']);\n\n    // Session should be available and populated again\n    expect($this->session)\n        ->getAll()->not()->toBe([]);\n\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    // Guard should pick up on the session\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe('hello|world|two');\n\n    // Directly wipe the Laravel session, circumventing the Guard APIs\n    $this->session->purge();\n\n    // Session should be empty\n    expect($this->session)\n        ->getAll()->toBe([]);\n\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_UNAUTHORIZED);\n\n    // Guard should have it's state emptied\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    $this->session->set('user', ['sub' => 'hello|world|4']);\n\n    // Session should be available\n    expect($this->session)\n        ->getAll()->not()->toBe([]);\n\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    // Guard should pick up on the session\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe('hello|world|4');\n\n    $identifier = uniqid('auth0|');\n    $user = new StatefulUser(['sub' => $identifier]);\n\n    // Overwrite state using the Guard's login()\n    $this->guard->login(CredentialEntity::create(\n        user: $user\n    ), Guard::SOURCE_SESSION);\n\n    getJson($this->route)\n        ->assertStatus(Response::HTTP_OK);\n\n    // Guard should have it's state updated\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe($identifier);\n\n    // Session should be updated\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier]);\n});\n\nit('creates a session from login()', function (): void {\n    $identifier = uniqid('auth0|');\n    $accessTokenScope = [uniqid('access-token-scope-')];\n    $accessTokenExpiration = time() + 60;\n\n    $this->session->set('user', ['sub' => $identifier]);\n    $this->session->set('accessTokenScope', $accessTokenScope);\n    $this->session->set('accessTokenExpiration', $accessTokenExpiration);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n\n    expect($found)\n        ->toBeInstanceOf(CredentialEntity::class);\n\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier])\n        ->get('accessTokenScope')->toBe($accessTokenScope)\n        ->get('accessTokenExpiration')->toBe($accessTokenExpiration)\n        ->get('refreshToken')->toBeNull();\n\n    $user = new StatefulUser(['sub' => $identifier]);\n\n    $changedRefreshToken = (string) Generator::create((createRsaKeys())->private);\n\n    // Overwrite state using the Guard's login()\n    $this->guard->login(CredentialEntity::create(\n        user: $user,\n        refreshToken: $changedRefreshToken\n    ), Guard::SOURCE_SESSION);\n\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe($identifier);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier])\n        ->get('accessTokenScope')->toBe($accessTokenScope)\n        ->get('accessTokenExpiration')->toBe($accessTokenExpiration)\n        ->get('refreshToken')->toBe($changedRefreshToken);\n});\n\nit('queries the /userinfo endpoint for refreshUser()', function (): void {\n    $identifier = uniqid('auth0|');\n    $accessTokenScope = [uniqid('access-token-scope-')];\n    $accessTokenExpiration = time() + 60;\n\n    $this->session->set('user', ['sub' => $identifier]);\n    $this->session->set('accessTokenScope', $accessTokenScope);\n    $this->session->set('accessTokenExpiration', $accessTokenExpiration);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier]);\n\n    $response = (new ResponseFactory)->createResponse();\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new StreamFactory)->createStream(\n                json_encode(\n                    value:         [\n                        'sub' => $identifier,\n                        'name' => 'John Doe',\n                        'email' => '...',\n                    ],\n                    flags: JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR\n                )\n            )\n        )\n    );\n\n    $this->guard->refreshUser();\n\n    $userAttributes = $this->guard->user()->getAttributes();\n\n    expect($userAttributes)\n        ->toBeArray()\n        ->toMatchArray([\n            'sub' => $identifier,\n            'name' => 'John Doe',\n            'email' => '...',\n        ]);\n});\n\nit('does not query the /userinfo endpoint for refreshUser() if an access token is not available', function (): void {\n    $identifier = uniqid('auth0|');\n    $accessTokenScope = [uniqid('access-token-scope-')];\n    $accessTokenExpiration = time() + 60;\n\n    $this->session->set('user', ['sub' => $identifier]);\n    $this->session->set('accessToken', null);\n    $this->session->set('accessTokenScope', $accessTokenScope);\n    $this->session->set('accessTokenExpiration', $accessTokenExpiration);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier]);\n\n    $response = (new ResponseFactory)->createResponse();\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new StreamFactory)->createStream(\n                json_encode(\n                    value:         [\n                        'sub' => $identifier,\n                        'name' => 'John Doe',\n                        'email' => '...',\n                    ],\n                    flags: JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR\n                )\n            )\n        )\n    );\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->setRequestLimit(-1);\n\n    $this->guard->refreshUser();\n\n    $userAttributes = $this->guard->user()->getAttributes();\n\n    expect($userAttributes)\n        ->toBeArray()\n        ->toMatchArray([\n            'sub' => $identifier,\n        ]);\n});\n\nit('rejects bad responses from the /userinfo endpoint for refreshUser()', function (): void {\n    $identifier = uniqid('auth0|');\n    $accessTokenScope = [uniqid('access-token-scope-')];\n    $accessTokenExpiration = time() + 60;\n\n    $this->session->set('user', ['sub' => $identifier]);\n    $this->session->set('accessTokenScope', $accessTokenScope);\n    $this->session->set('accessTokenExpiration', $accessTokenExpiration);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->session)\n        ->get('user')->toBe(['sub' => $identifier]);\n\n    $response = (new ResponseFactory)->createResponse();\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new StreamFactory)->createStream(\n                json_encode(\n                    value: 'bad response',\n                    flags: JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR\n                )\n            )\n        )\n    );\n\n    $this->guard->refreshUser();\n\n    $userAttributes = $this->guard->user()->getAttributes();\n\n    expect($userAttributes)\n        ->toBeArray()\n        ->toMatchArray([\n            'sub' => $identifier,\n        ]);\n});\n\nit('immediately invalidates an expired session when a refresh token is not available', function (): void {\n    $this->session->set('accessTokenExpiration', time() - 1000);\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    expect($this->session)\n        ->get('user')->toBeNull();\n});\n\nit('invalidates an expired session when an access token fails to refresh', function (): void {\n    $this->session->set('accessTokenExpiration', time() - 1000);\n    $this->session->set('refreshToken', uniqid());\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    expect($this->session)\n        ->get('user')->toBeNull();\n});\n\nit('successfully continues a session when an access token succeeds is renewed', function (): void {\n    $this->session->set('accessTokenExpiration', time() - 1000);\n    $this->session->set('refreshToken', uniqid());\n\n    $response = (new ResponseFactory)->createResponse();\n\n    $token = Generator::create((createRsaKeys())->private, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        \"sub\" => \"auth0|123456\",\n        \"aud\" => [\n          \"https://example.com/health-api\",\n          \"https://my-domain.auth0.com/userinfo\",\n          config('auth0.guards.default.clientId')\n        ],\n        \"azp\" => config('auth0.guards.default.clientId'),\n        \"exp\" => time() + 60,\n        \"iat\" => time(),\n        \"scope\" => \"openid profile read:patients read:admin\"\n    ]);\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new StreamFactory)->createStream(\n                json_encode(\n                    value: [\n                        'access_token' => $token->toString(),\n                        'expires_in' => 60,\n                        'scope' => 'openid profile',\n                        'token_type' => 'Bearer',\n                    ],\n                    flags: JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR\n                )\n            )\n        )\n    );\n\n    $found = $this->guard->find(Guard::SOURCE_SESSION);\n    $this->guard->login($found, Guard::SOURCE_SESSION);\n\n    expect($this->guard)\n        ->user()->not()->toBeNull();\n\n    expect($this->session)\n        ->get('user')->not()->toBeNull();\n});\n\nit('returns a consistent HMAC from hashPasswordForCookie()', function (): void {\n    config(['app.key' => 'test-app-key']);\n\n    $hash = $this->guard->hashPasswordForCookie('password-hash');\n\n    expect($hash)\n        ->toBeString()\n        ->toBe(hash_hmac('sha256', 'password-hash', 'test-app-key'));\n});\n\nit('returns different HMACs for different passwords from hashPasswordForCookie()', function (): void {\n    config(['app.key' => 'test-app-key']);\n\n    $hash1 = $this->guard->hashPasswordForCookie('password-one');\n    $hash2 = $this->guard->hashPasswordForCookie('password-two');\n\n    expect($hash1)->not()->toBe($hash2);\n});\n\nit('returns different HMACs for different app keys from hashPasswordForCookie()', function (): void {\n    config(['app.key' => 'key-one']);\n    $hash1 = $this->guard->hashPasswordForCookie('password-hash');\n\n    config(['app.key' => 'key-two']);\n    $hash2 = $this->guard->hashPasswordForCookie('password-hash');\n\n    expect($hash1)->not()->toBe($hash2);\n});\n\nit('uses fallback key when app.key is null in hashPasswordForCookie()', function (): void {\n    config(['app.key' => null]);\n\n    $hash = $this->guard->hashPasswordForCookie('password-hash');\n\n    expect($hash)\n        ->toBeString()\n        ->toBe(hash_hmac('sha256', 'password-hash', 'base-key-for-password-hash-mac'));\n});\n"
  },
  {
    "path": "tests/Unit/Guards/AuthorizationGuardTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\Entities\\CredentialEntity;\nuse Auth0\\Laravel\\Users\\StatelessUser;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\nuse Auth0\\SDK\\Token;\nuse Auth0\\SDK\\Token\\Generator;\nuse PsrMock\\Psr18\\Client as MockHttpClient;\nuse PsrMock\\Psr17\\RequestFactory as MockRequestFactory;\nuse PsrMock\\Psr17\\ResponseFactory as MockResponseFactory;\nuse PsrMock\\Psr17\\StreamFactory as MockStreamFactory;\n\nuse function Pest\\Laravel\\getJson;\n\nuses()->group('auth', 'auth.guard', 'auth.guard.session');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.audience' => ['https://example.com/health-api'],\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = $guard = auth('auth0-api');\n    $this->sdk = $this->laravel->getSdk();\n    $this->config = $this->sdk->configuration();\n\n    $this->identifier = 'auth0|' . uniqid();\n\n    $this->token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        \"sub\" => $this->identifier,\n        \"aud\" => [\n            config('auth0.guards.default.audience')[0],\n            \"https://my-domain.auth0.com/userinfo\"\n        ],\n        \"azp\" => config('auth0.guards.default.clientId'),\n        \"exp\" => time() + 60,\n        \"iat\" => time(),\n        \"scope\" => \"openid profile read:patients read:admin\"\n    ]);\n\n    $this->bearerToken = ['Authorization' => 'Bearer ' . $this->token->toString()];\n\n    $this->route = '/' . uniqid();\n    $this->route2 = '/' . uniqid();\n    $guard = $this->guard;\n\n    Route::get($this->route, function () use ($guard) {\n        $credential = $guard->find(Guard::SOURCE_TOKEN);\n\n        if (null !== $credential) {\n            $guard->login($credential, Guard::SOURCE_TOKEN);\n\n            return response()->json(['status' => 'OK', 'user' => $guard->user(Guard::SOURCE_TOKEN)->getAuthIdentifier()]);\n        }\n\n        abort(Response::HTTP_UNAUTHORIZED, 'Unauthorized');\n    });\n\n    Route::get($this->route2, function () use ($guard) {\n        return response()->json(['user' => $guard->user(Guard::SOURCE_TOKEN)?->getAuthIdentifier()]);\n    });\n});\n\nit('assigns a user with login() from a good token', function (): void {\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    getJson($this->route, $this->bearerToken)\n        ->assertStatus(Response::HTTP_OK);\n\n    expect($this->guard)\n        ->user()->not()->toBeNull();\n});\n\nit('assigns a user with user() from a good token', function (): void {\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    getJson($this->route2, $this->bearerToken)\n        ->assertStatus(Response::HTTP_OK);\n\n    expect($this->guard)\n        ->user()->not()->toBeNull();\n});\n\n// it('does not assign a user from a empty token', function (): void {\n//     getJson($this->route, ['Authorization' => 'Bearer '])\n//         ->assertStatus(Response::HTTP_UNAUTHORIZED);\n\n//     expect($this->guard)\n//         ->user()->toBeNull();\n// });\n\n// it('does not get a user from a bad token', function (): void {\n//     $this->config->setAudience(['BAD_AUDIENCE']);\n\n//     expect($this->guard)\n//         ->user()->toBeNull();\n\n//     getJson($this->route, $this->bearerToken)\n//         ->assertStatus(Response::HTTP_UNAUTHORIZED);\n\n//     expect($this->guard)\n//         ->user()->toBeNull();\n// });\n\nit('does not query the /userinfo endpoint for refreshUser() without a bearer token', function (): void {\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    $this->guard->setCredential(new CredentialEntity(\n        user: new StatelessUser(['sub' => $this->identifier]),\n    ));\n\n    expect($this->guard)\n        ->user()->not()->toBeNull();\n\n    $client = new MockHttpClient(requestLimit: 0);\n\n    $this->config->setHttpClient($client);\n\n    $this->guard->refreshUser();\n\n    expect($this->guard)\n        ->user()->not()->toBeNull();\n});\n\nit('aborts querying the /userinfo endpoint for refreshUser() when a bad response is received', function (): void {\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    getJson($this->route2, $this->bearerToken)\n        ->assertStatus(Response::HTTP_OK);\n\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe($this->identifier);\n\n    $response = (new MockResponseFactory)->createResponse();\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new MockStreamFactory)->createStream(\n                json_encode(\n                    value: true,\n                    flags: JSON_PRETTY_PRINT\n                )\n            )\n        )\n    );\n\n    $this->guard->refreshUser();\n\n    $userAttributes = $this->guard->user()->getAttributes();\n\n    expect($userAttributes)\n        ->toBeArray()\n        ->toHaveKey('sub', $this->identifier);\n});\n\nit('queries the /userinfo endpoint for refreshUser()', function (): void {\n    expect($this->guard)\n        ->user()->toBeNull();\n\n    getJson($this->route2, $this->bearerToken)\n        ->assertStatus(Response::HTTP_OK);\n\n    expect($this->guard)\n        ->user()->getAuthIdentifier()->toBe($this->identifier);\n\n    $response = (new MockResponseFactory)->createResponse();\n\n    $this->guard\n        ->sdk()\n        ->configuration()\n        ->getHttpClient()\n        ->addResponseWildcard($response->withBody(\n            (new MockStreamFactory)->createStream(\n                json_encode(\n                    value: [\n                        'sub' => $this->identifier,\n                        'name' => 'John Doe',\n                        'email' => '...',\n                    ],\n                    flags: JSON_PRETTY_PRINT\n                )\n            )\n        )\n    );\n\n    $this->guard->refreshUser();\n\n    $userAttributes = $this->guard->user()->getAttributes();\n\n    expect($userAttributes)\n        ->toBeArray()\n        ->toMatchArray([\n            'sub' => $this->identifier,\n            'name' => 'John Doe',\n            'email' => '...',\n        ]);\n});\n"
  },
  {
    "path": "tests/Unit/Middleware/AuthenticateMiddlewareTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Users\\UserContract;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('stateful', 'middleware', 'middleware.stateful', 'middleware.stateful.authenticate');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $this->validSession = [\n        'auth0_session' => json_encode([\n            'user' => ['sub' => 'hello|world'],\n            'idToken' => (string) Generator::create((createRsaKeys())->private),\n            'accessToken' => (string) Generator::create((createRsaKeys())->private),\n            'accessTokenScope' => [uniqid(), 'read:admin'],\n            'accessTokenExpiration' => time() + 60,\n        ])\n    ];\n});\n\nit('redirects to login route if a visitor does not have a session', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    $this->withoutExceptionHandling()\n         ->get($route)\n         ->assertRedirect('/login');\n\n    expect(redirect()->getIntendedUrl())\n        ->toEqual('http://localhost' . $route);\n\n    expect($this->guard)\n         ->user()->toBeNull();\n});\n\nit('assigns a user', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    $this->withSession($this->validSession)\n         ->get($route)\n            ->assertStatus(Response::HTTP_OK)\n            ->assertSee($route);\n\n    expect($this->guard)\n         ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('assigns a user when using a configured scope matches', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate:read:admin')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    $this->withSession($this->validSession)\n         ->get($route)\n            ->assertStatus(Response::HTTP_OK)\n            ->assertSee($route);\n\n    expect($this->guard)\n         ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('does not assign a user when a configured scope is not matched', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate:something:else')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    $this->withSession($this->validSession)\n         ->get($route)\n         ->assertStatus(Response::HTTP_FORBIDDEN);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n\nit('does not assign a user when an incompatible guard is used', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    config($config = [\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null\n    ]);\n\n    $this->get($route)\n         ->assertStatus(Response::HTTP_INTERNAL_SERVER_ERROR);\n\n    expect($this->guard)\n         ->user()->toBeNull();\n});\n"
  },
  {
    "path": "tests/Unit/Middleware/AuthenticateOptionalMiddlewareTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Users\\UserContract;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('stateful', 'middleware', 'middleware.stateful', 'middleware.stateful.authenticate_optional');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $this->validSession = [\n        'auth0_session' => json_encode([\n            'user' => ['sub' => 'hello|world'],\n            'idToken' => (string) Generator::create((createRsaKeys())->private),\n            'accessToken' => (string) Generator::create((createRsaKeys())->private),\n            'accessTokenScope' => [uniqid(), 'read:admin'],\n            'accessTokenExpiration' => time() + 60,\n        ]),\n    ];\n});\n\nit('continues if a visitor does not have a session', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate.optional')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    $this->get($route)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertSee($route);\n\n    expect($this->guard)\n         ->user()->toBeNull();\n});\n\nit('assigns a user', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate.optional')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    $this->withSession($this->validSession)\n         ->get($route)\n             ->assertStatus(Response::HTTP_OK)\n             ->assertSee($route);\n\n    expect($this->guard)\n         ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('assigns a user when using a configured scope matches', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate.optional:read:admin')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    $this->withSession($this->validSession)\n         ->get($route)\n             ->assertStatus(Response::HTTP_OK)\n             ->assertSee($route);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('does not assign a user when a configured scope is not matched', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate.optional:something:else')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    $this->withSession($this->validSession)\n         ->get($route)\n             ->assertStatus(Response::HTTP_OK)\n             ->assertSee($route);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n\nit('does not assign a user when an incompatible guard is used', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate.optional')->get($route, function () use ($route): string {\n        return $route;\n    });\n\n    config($config = [\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null\n    ]);\n\n    $this->get($route)\n         ->assertStatus(Response::HTTP_INTERNAL_SERVER_ERROR);\n\n    expect($this->guard)\n         ->user()->toBeNull();\n});\n"
  },
  {
    "path": "tests/Unit/Middleware/AuthenticatorMiddlewareTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\ServiceProvider;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('Middleware/AuthenticatorMiddleware');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_NONE,\n    ]);\n});\n\nit('installs the Auth0 Authenticator', function (): void {\n    config(['auth.defaults.guard' => 'web']);\n    app(ServiceProvider::class, ['app' => app()])->registerMiddleware(app('router'));\n\n    Route::middleware('web')->get('/test', function (): JsonResponse {\n        if (auth()->guard()->name !== 'auth0-session') {\n            abort(Response::HTTP_INTERNAL_SERVER_ERROR);\n        }\n\n        return response()->json([\n            'guard' => auth()->guard()->name,\n            'middleware' => app('router')->getMiddlewareGroups()\n        ]);\n    });\n\n    $this->get('/test')\n         ->assertOK();\n});\n"
  },
  {
    "path": "tests/Unit/Middleware/AuthorizeMiddlewareTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Users\\UserContract;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('stateful', 'middleware', 'middleware.stateless', 'middleware.stateless.authorize');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n    $this->domain = uniqid() . '.auth0.com';\n    $this->clientId = uniqid();\n    $this->audience = [uniqid()];\n    $this->cookieSecret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.default.domain' => $this->domain,\n        'auth0.guards.default.clientId' => $this->clientId,\n        'auth0.guards.default.audience' => $this->audience,\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => $this->cookieSecret,\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n        // Also configure 'web' since legacyGuard uses configuration => 'web'\n        'auth0.guards.web.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.web.domain' => $this->domain,\n        'auth0.guards.web.clientId' => $this->clientId,\n        'auth0.guards.web.audience' => $this->audience,\n        'auth0.guards.web.clientSecret' => $this->secret,\n        'auth0.guards.web.cookieSecret' => $this->cookieSecret,\n        'auth0.guards.web.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n});\n\nit('does not assign a user when an incompatible guard is used', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    config([\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null\n    ]);\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . uniqid()])\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route]);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n\nit('returns a 401 and does not assign a user when an invalid bearer token is provided', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . uniqid()])\n        ->assertStatus(Response::HTTP_UNAUTHORIZED);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n\nit('assigns a user', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    $token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        \"sub\" => \"auth0|123456\",\n        \"aud\" => array_merge(\n          $this->audience,\n          [config('auth0.guards.default.clientId')]\n        ),\n        \"azp\" => config('auth0.guards.default.clientId'),\n        \"exp\" => time() + 60,\n        \"iat\" => time(),\n        \"scope\" => \"openid profile read:patients read:admin\"\n    ]);\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . $token->toString()])\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('assigns a user when using a configured scope matches', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize:read:admin')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    $token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        \"sub\" => \"auth0|123456\",\n        \"aud\" => array_merge(\n          $this->audience,\n          [config('auth0.guards.default.clientId')]\n        ),\n        \"azp\" => config('auth0.guards.default.clientId'),\n        \"exp\" => time() + 60,\n        \"iat\" => time(),\n        \"scope\" => \"openid profile read:patients read:admin\"\n    ]);\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . $token->toString()])\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('returns a 403 and does not assign a user when a configured scope is not matched', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize:something:else')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    $token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        \"sub\" => \"auth0|123456\",\n        \"aud\" => array_merge(\n          $this->audience,\n          [config('auth0.guards.default.clientId')]\n        ),\n        \"azp\" => config('auth0.guards.default.clientId'),\n        \"exp\" => time() + 60,\n        \"iat\" => time(),\n        \"scope\" => \"openid profile read:patients read:admin\"\n    ]);\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . $token->toString()])\n        ->assertStatus(Response::HTTP_FORBIDDEN);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n"
  },
  {
    "path": "tests/Unit/Middleware/AuthorizeOptionalMiddlewareTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Users\\UserContract;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('stateful', 'middleware', 'middleware.stateless', 'middleware.stateless.authorize');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n    $this->domain = uniqid() . '.auth0.com';\n    $this->clientId = uniqid();\n    $this->audience = [uniqid()];\n    $this->cookieSecret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.default.domain' => $this->domain,\n        'auth0.guards.default.clientId' => $this->clientId,\n        'auth0.guards.default.audience' => $this->audience,\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => $this->cookieSecret,\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n        // Also configure 'web' since legacyGuard uses configuration => 'web'\n        'auth0.guards.web.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.web.domain' => $this->domain,\n        'auth0.guards.web.clientId' => $this->clientId,\n        'auth0.guards.web.audience' => $this->audience,\n        'auth0.guards.web.clientSecret' => $this->secret,\n        'auth0.guards.web.cookieSecret' => $this->cookieSecret,\n        'auth0.guards.web.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n});\n\nit('does not assign a user when an invalid bearer token is provided', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize.optional')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . uniqid()])\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route]);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n\nit('assigns a user', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize.optional')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    $token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        \"sub\" => \"auth0|123456\",\n        \"aud\" => array_merge(\n          $this->audience,\n          [config('auth0.guards.default.clientId')]\n        ),\n        \"azp\" => config('auth0.guards.default.clientId'),\n        \"exp\" => time() + 60,\n        \"iat\" => time(),\n        \"scope\" => \"openid profile read:patients read:admin\"\n    ]);\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . $token->toString()])\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('assigns a user when using a configured scope matches', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize.optional:read:admin')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    $token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        \"sub\" => \"auth0|123456\",\n        \"aud\" => array_merge(\n          $this->audience,\n          [config('auth0.guards.default.clientId')]\n        ),\n        \"azp\" => config('auth0.guards.default.clientId'),\n        \"exp\" => time() + 60,\n        \"iat\" => time(),\n        \"scope\" => \"openid profile read:patients read:admin\"\n    ]);\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . $token->toString()])\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('does not assign a user when a configured scope is not matched', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize.optional:something:else')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    $token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        \"sub\" => \"auth0|123456\",\n        \"aud\" => array_merge(\n          $this->audience,\n          [config('auth0.guards.default.clientId')]\n        ),\n        \"azp\" => config('auth0.guards.default.clientId'),\n        \"exp\" => time() + 60,\n        \"iat\" => time(),\n        \"scope\" => \"openid profile read:patients read:admin\"\n    ]);\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . $token->toString()])\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route]);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n\nit('does not assign a user when an incompatible guard is used', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize.optional')->get($route, function () use ($route): string {\n        return json_encode(['status' => $route]);\n    });\n\n    config([\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null\n    ]);\n\n    $this->getJson($route, ['Authorization' => 'Bearer ' . uniqid()])\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route]);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n"
  },
  {
    "path": "tests/Unit/Middleware/AuthorizerMiddlewareTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\ServiceProvider;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('Middleware/AuthenticatorMiddleware');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_NONE,\n    ]);\n});\n\nit('installs the Auth0 Authenticator', function (): void {\n    config(['auth.defaults.guard' => 'web']);\n    app(ServiceProvider::class, ['app' => app()])->registerMiddleware(app('router'));\n\n    Route::middleware('api')->get('/test', function (): JsonResponse {\n        if (auth()->guard()->name !== 'auth0-api') {\n            abort(Response::HTTP_INTERNAL_SERVER_ERROR);\n        }\n\n        return response()->json([\n            'guard' => auth()->guard()->name,\n            'middleware' => app('router')->getMiddlewareGroups()\n        ]);\n    });\n\n    $this->get('/test')\n         ->assertOK();\n});\n"
  },
  {
    "path": "tests/Unit/Middleware/GuardMiddlewareTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('middleware', 'middleware.guard');\n\nbeforeEach(function (): void {\n    $this->laravel = app('auth0');\n});\n\nit('assigns the guard for route handling', function (): void {\n    $routeMiddlewareAssignedGuard = '/' . uniqid();\n    $routeMiddlewareUnassignedGuard = '/' . uniqid();\n    $routeUnspecifiedGuard = '/' . uniqid();\n\n    $defaultGuardClass = 'Illuminate\\Auth\\SessionGuard';\n    $sdkGuardClass = 'Auth0\\Laravel\\Auth\\Guard';\n\n    config(['auth.defaults.guard' => 'web']);\n\n    Route::get($routeUnspecifiedGuard, function (): string {\n        return get_class(auth()->guard());\n    });\n\n    Route::middleware('guard:legacyGuard')->get($routeMiddlewareAssignedGuard, function (): string {\n        return get_class(auth()->guard());\n    });\n\n    Route::middleware('guard')->get($routeMiddlewareUnassignedGuard, function (): string {\n        return get_class(auth()->guard());\n    });\n\n    $this->get($routeUnspecifiedGuard)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertSee($defaultGuardClass);\n\n    $this->get($routeMiddlewareAssignedGuard)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertSee($sdkGuardClass);\n\n    $this->get($routeUnspecifiedGuard)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertSee($sdkGuardClass);\n\n    $this->get($routeMiddlewareUnassignedGuard)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertSee($defaultGuardClass);\n\n    $this->get($routeUnspecifiedGuard)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertSee($defaultGuardClass);\n});\n\nit('assigns the guard for route group handling', function (): void {\n    $routeMiddlewareUnassignedGuard = '/' . uniqid();\n    $routeUnspecifiedGuard = '/' . uniqid();\n\n    $defaultGuardClass = 'Illuminate\\Auth\\SessionGuard';\n    $sdkGuardClass = 'Auth0\\Laravel\\Auth\\Guard';\n\n    config(['auth.defaults.guard' => 'web']);\n\n    Route::middleware('guard:legacyGuard')->group(function () use ($routeUnspecifiedGuard, $routeMiddlewareUnassignedGuard) {\n        Route::get($routeUnspecifiedGuard, function (): string {\n            return get_class(auth()->guard());\n        });\n\n        Route::middleware('guard')->get($routeMiddlewareUnassignedGuard, function (): string {\n            return get_class(auth()->guard());\n        });\n    });\n\n    $this->get($routeUnspecifiedGuard)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertSee($sdkGuardClass);\n\n    $this->get($routeMiddlewareUnassignedGuard)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertSee($defaultGuardClass);\n\n    $this->get($routeUnspecifiedGuard)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertSee($sdkGuardClass);\n});\n"
  },
  {
    "path": "tests/Unit/ServiceProviderTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Auth0;\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\Bridges\\{CacheBridge, CacheItemBridge, SessionBridge};\nuse Auth0\\Laravel\\Configuration;\nuse Auth0\\Laravel\\Controllers\\{CallbackController, LoginController, LogoutController};\nuse Auth0\\Laravel\\Entities\\CredentialEntity;\nuse Auth0\\Laravel\\Guards\\{AuthenticationGuard, AuthorizationGuard};\nuse Auth0\\Laravel\\Middleware\\{AuthenticateMiddleware, AuthenticateOptionalMiddleware, AuthenticatorMiddleware, AuthorizeMiddleware, AuthorizeOptionalMiddleware, AuthorizerMiddleware, GuardMiddleware};\nuse Auth0\\Laravel\\Service;\nuse Auth0\\Laravel\\ServiceProvider;\nuse Auth0\\Laravel\\UserProvider;\nuse Auth0\\Laravel\\UserRepository;\nuse Auth0\\Laravel\\Users\\ImposterUser;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Support\\Facades\\Gate;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('ServiceProvider');\n\nfunction setupGuardImpersonation(\n    array $profile = [],\n    array $scope = [],\n    array $permissions = [],\n): Authenticatable {\n    Auth::shouldUse('legacyGuard');\n\n    $imposter = new ImposterUser(array_merge([\n        'sub' => uniqid(),\n        'name' => uniqid(),\n        'email' => uniqid() . '@example.com',\n    ], $profile));\n\n    Auth::guard()->setImpersonating(CredentialEntity::create(\n        user: $imposter,\n        idToken: (string) Generator::create((createRsaKeys())->private),\n        accessToken: (string) Generator::create((createRsaKeys())->private),\n        accessTokenScope: $scope,\n        accessTokenDecoded: [\n            'permissions' => $permissions,\n        ],\n    ));\n\n    return $imposter;\n}\n\nfunction resetGuard(\n    ?Authenticatable $imposter = null,\n): void {\n    Auth::shouldUse('web');\n\n    if (null !== $imposter) {\n        Auth::setUser($imposter);\n    }\n\n    config([\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null,\n    ]);\n}\n\nit('provides the expected classes', function (): void {\n    $service = app(ServiceProvider::class, ['app' => $this->app]);\n\n    expect($service->provides())\n        ->toBe([\n            Auth0::class,\n            AuthenticateMiddleware::class,\n            AuthenticateOptionalMiddleware::class,\n            AuthenticationGuard::class,\n            AuthenticatorMiddleware::class,\n            AuthorizationGuard::class,\n            AuthorizeMiddleware::class,\n            AuthorizeOptionalMiddleware::class,\n            AuthorizerMiddleware::class,\n            CacheBridge::class,\n            CacheItemBridge::class,\n            CallbackController::class,\n            Configuration::class,\n            Guard::class,\n            GuardMiddleware::class,\n            LoginController::class,\n            LogoutController::class,\n            Service::class,\n            SessionBridge::class,\n            UserProvider::class,\n            UserRepository::class,\n        ]);\n});\n\nit('creates a Service singleton with `auth0` alias', function (): void {\n    $singleton1 = $this->app->make('auth0');\n    $singleton2 = $this->app->make(Service::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(Service::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(Service::class);\n\n    expect($singleton1)\n        ->toBe($singleton2);\n});\n\nit('does NOT create a Guard singleton', function (): void {\n    $singleton1 = auth()->guard('legacyGuard');\n    $singleton2 = $this->app->make(Guard::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(Guard::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(Guard::class);\n\n    expect($singleton1)\n        ->not()->toBe($singleton2);\n});\n\nit('creates a UserRepository singleton', function (): void {\n    $singleton1 = $this->app->make('auth0.repository');\n    $singleton2 = $this->app->make(UserRepository::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(UserRepository::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(UserRepository::class);\n\n    expect($singleton1)\n        ->toBe($singleton2);\n});\n\nit('does NOT a Provider singleton', function (): void {\n    $singleton1 = Auth::createUserProvider('auth0-provider');\n    $singleton2 = $this->app->make(UserProvider::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(UserProvider::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(UserProvider::class);\n\n    expect($singleton1)\n        ->not()->toBe($singleton2);\n});\n\nit('creates a AuthenticateMiddleware singleton', function (): void {\n    $singleton1 = $this->app->make(AuthenticateMiddleware::class);\n    $singleton2 = $this->app->make(AuthenticateMiddleware::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(AuthenticateMiddleware::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(AuthenticateMiddleware::class);\n\n    expect($singleton1)\n        ->toBe($singleton2);\n});\n\nit('creates a AuthenticateOptionalMiddleware singleton', function (): void {\n    $singleton1 = $this->app->make(AuthenticateOptionalMiddleware::class);\n    $singleton2 = $this->app->make(AuthenticateOptionalMiddleware::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(AuthenticateOptionalMiddleware::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(AuthenticateOptionalMiddleware::class);\n\n    expect($singleton1)\n        ->toBe($singleton2);\n});\n\nit('creates a AuthorizeMiddleware singleton', function (): void {\n    $singleton1 = $this->app->make(AuthorizeMiddleware::class);\n    $singleton2 = $this->app->make(AuthorizeMiddleware::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(AuthorizeMiddleware::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(AuthorizeMiddleware::class);\n\n    expect($singleton1)\n        ->toBe($singleton2);\n});\n\nit('creates a AuthorizeOptionalMiddleware singleton', function (): void {\n    $singleton1 = $this->app->make(AuthorizeOptionalMiddleware::class);\n    $singleton2 = $this->app->make(AuthorizeOptionalMiddleware::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(AuthorizeOptionalMiddleware::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(AuthorizeOptionalMiddleware::class);\n\n    expect($singleton1)\n        ->toBe($singleton2);\n});\n\nit('creates a LoginController singleton', function (): void {\n    $singleton1 = $this->app->make(LoginController::class);\n    $singleton2 = $this->app->make(LoginController::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(LoginController::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(LoginController::class);\n\n    expect($singleton1)\n        ->toBe($singleton2);\n});\n\nit('creates a LogoutController singleton', function (): void {\n    $singleton1 = $this->app->make(LogoutController::class);\n    $singleton2 = $this->app->make(LogoutController::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(LogoutController::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(LogoutController::class);\n\n    expect($singleton1)\n        ->toBe($singleton2);\n});\n\nit('creates a CallbackController singleton', function (): void {\n    $singleton1 = $this->app->make(CallbackController::class);\n    $singleton2 = $this->app->make(CallbackController::class);\n\n    expect($singleton1)\n        ->toBeInstanceOf(CallbackController::class);\n\n    expect($singleton2)\n        ->toBeInstanceOf(CallbackController::class);\n\n    expect($singleton1)\n        ->toBe($singleton2);\n});\n\ntest('Gate::check(`scope`) returns true when a match hits', function (): void {\n    setupGuardImpersonation(\n        scope: [uniqid(), 'testScope', uniqid()],\n    );\n\n    expect(Gate::check('scope', 'testScope'))\n        ->toBeTrue();\n});\n\ntest('Gate::check(`scope`) returns false when a match misses', function (): void {\n    setupGuardImpersonation(\n        scope: [uniqid()],\n    );\n\n    expect(Gate::check('scope', 'testScope'))\n        ->toBeFalse();\n});\n\ntest('Gate::check(`scope`) returns false when an incompatible Guard is used', function (): void {\n    $imposter = setupGuardImpersonation(\n        scope: [uniqid(), 'testScope', uniqid()],\n    );\n\n    resetGuard($imposter);\n\n    expect(Gate::check('scope', 'testScope'))\n        ->toBeFalse();\n});\n\ntest('Gate::check(`permission`) returns true when a match hits', function (): void {\n    setupGuardImpersonation(\n        permissions: [uniqid(), 'testPermission', uniqid()],\n    );\n\n    expect(Gate::check('permission', 'testPermission'))\n        ->toBeTrue();\n});\n\ntest('Gate::check(`permission`) returns false when a match misses', function (): void {\n    setupGuardImpersonation(\n        permissions: [uniqid()],\n    );\n\n    expect(Gate::check('permission', 'testPermission'))\n        ->toBeFalse();\n});\n\ntest('Gate::check(`permission`) returns false when an incompatible Guard is used', function (): void {\n    $imposter = setupGuardImpersonation(\n        permissions: [uniqid(), 'testPermission', uniqid()],\n    );\n\n    resetGuard($imposter);\n\n    expect(Gate::check('permission', 'testPermission'))\n        ->toBeFalse();\n});\n\ntest('policies `can(scope)` middleware returns true when a match hits', function (): void {\n    Route::get('/test', function () {\n        return response()->json(['status' => 'OK']);\n    })->can('scope:testScope');\n\n    setupGuardImpersonation(\n        scope: [uniqid(), 'testScope', uniqid()],\n    );\n\n    $this->getJson('/test')\n         ->assertOK();\n});\n\ntest('policies `can(scope)` middleware returns false when a match misses', function (): void {\n    Route::get('/test', function () {\n    })->can('scope:testScope');\n\n    setupGuardImpersonation(\n        scope: [uniqid()],\n    );\n\n    $this->getJson('/test')\n         ->assertStatus(Response::HTTP_FORBIDDEN);\n});\n\ntest('policies `can(scope)` middleware returns false when an incompatible Guard is used', function (): void {\n    Route::get('/test', function () {\n        return response()->json(['status' => 'OK']);\n    })->can('scope:testScope');\n\n    $imposter = setupGuardImpersonation(\n        scope: [uniqid(), 'testScope', uniqid()],\n    );\n\n    resetGuard($imposter);\n\n    $this->getJson('/test')\n         ->assertStatus(Response::HTTP_FORBIDDEN);\n});\n\ntest('policies `can(permission)` middleware returns true when a match hits', function (): void {\n    Route::get('/test', function () {\n        return response()->json(['status' => 'OK']);\n    })->can('testing:123');\n\n    setupGuardImpersonation(\n        permissions: [uniqid(), 'testing:123', uniqid()],\n    );\n\n    $this->getJson('/test')\n         ->assertOK();\n});\n\ntest('policies `can(permission)` middleware returns false when a match misses', function (): void {\n    Route::get('/test', function () {\n        return response()->json(['status' => 'OK']);\n    })->can('testing:123');\n\n    setupGuardImpersonation(\n        permissions: [uniqid(), 'testing:456', uniqid()],\n    );\n\n    $this->getJson('/test')\n    ->assertStatus(Response::HTTP_FORBIDDEN);\n});\n\ntest('policies `can(permission)` middleware returns false when an incompatible Guard is used', function (): void {\n    Route::get('/test', function () {\n        return response()->json(['status' => 'OK']);\n    })->can('testing:123');\n\n    $imposter = setupGuardImpersonation(\n        permissions: [uniqid(), 'testing:123', uniqid()],\n    );\n\n    resetGuard($imposter);\n\n    $this->getJson('/test')\n    ->assertStatus(Response::HTTP_FORBIDDEN);\n});\n\ntest('auth0.registerGuards === true registers guards', function (): void {\n    config(['auth0.registerGuards' => true]);\n\n    $service = app(ServiceProvider::class, ['app' => $this->app]);\n    /**\n     * @var ServiceProvider $service\n     */\n    $service->register();\n\n    expect(config('auth.guards.auth0-session'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'auth0.authenticator')\n        ->toHaveKey('configuration', 'web')\n        ->toHaveKey('provider', 'auth0-provider');\n\n    expect(config('auth.guards.auth0-api'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'auth0.authorizer')\n        ->toHaveKey('configuration', 'api')\n        ->toHaveKey('provider', 'auth0-provider');\n\n    expect(config('auth.providers.auth0-provider'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'auth0.provider')\n        ->toHaveKey('repository', 'auth0.repository');\n});\n\ntest('auth0.registerGuards === true registers guards, but does not overwrite an existing auth.guards.auth0-session entry', function (): void {\n    config([\n        'auth0.registerGuards' => true,\n        'auth.guards.auth0-session' => [\n            'driver' => 'session',\n            'provider' => 'users',\n        ]\n    ]);\n\n    $service = app(ServiceProvider::class, ['app' => $this->app]);\n    /**\n     * @var ServiceProvider $service\n     */\n    $service->register();\n\n    expect(config('auth.guards.auth0-session'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'session')\n        ->toHaveKey('provider', 'users')\n        ->not()->toHaveKey('configuration');\n\n    expect(config('auth.guards.auth0-api'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'auth0.authorizer')\n        ->toHaveKey('configuration', 'api')\n        ->toHaveKey('provider', 'auth0-provider');\n\n    expect(config('auth.providers.auth0-provider'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'auth0.provider')\n        ->toHaveKey('repository', 'auth0.repository');\n});\n\ntest('auth0.registerGuards === true registers guards, but does not overwrite an existing auth.guards.auth0-api entry', function (): void {\n    config([\n        'auth0.registerGuards' => true,\n        'auth.guards.auth0-api' => [\n            'driver' => 'api',\n            'provider' => 'users',\n        ]\n    ]);\n\n    $service = app(ServiceProvider::class, ['app' => $this->app]);\n    /**\n     * @var ServiceProvider $service\n     */\n    $service->register();\n\n    expect(config('auth.guards.auth0-session'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'auth0.authenticator')\n        ->toHaveKey('configuration', 'web')\n        ->toHaveKey('provider', 'auth0-provider');\n\n    expect(config('auth.guards.auth0-api'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'api')\n        ->toHaveKey('provider', 'users')\n        ->not()->toHaveKey('configuration');\n\n    expect(config('auth.providers.auth0-provider'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'auth0.provider')\n        ->toHaveKey('repository', 'auth0.repository');\n});\n\ntest('auth0.registerGuards === true registers guards, but does not overwrite an existing auth.providers.auth0-provider entry', function (): void {\n    config([\n        'auth0.registerGuards' => true,\n        'auth.providers.auth0-provider' => [\n            'driver' => 'database',\n            'repository' => 'users',\n        ]\n    ]);\n\n    $service = app(ServiceProvider::class, ['app' => $this->app]);\n    /**\n     * @var ServiceProvider $service\n     */\n    $service->register();\n\n    expect(config('auth.guards.auth0-session'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'auth0.authenticator')\n        ->toHaveKey('configuration', 'web')\n        ->toHaveKey('provider', 'auth0-provider');\n\n        expect(config('auth.guards.auth0-api'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'auth0.authorizer')\n        ->toHaveKey('configuration', 'api')\n        ->toHaveKey('provider', 'auth0-provider');\n\n    expect(config('auth.providers.auth0-provider'))\n        ->toBeArray()\n        ->toHaveKey('driver', 'database')\n        ->toHaveKey('repository', 'users');\n});\n\ntest('auth0.registerMiddleware === true registers middleware', function (): void {\n    config(['auth0.registerMiddleware' => true]);\n\n    $service = app(ServiceProvider::class, ['app' => $this->app]);\n    /**\n     * @var ServiceProvider $service\n     */\n    $service->registerMiddleware(app('router'));\n    /**\n     * @var \\Illuminate\\Foundation\\Http\\Kernel $kernel\n     */\n    $middleware = app('router')->getMiddlewareGroups();\n\n    expect($middleware)\n        ->toBeArray()\n        ->toHaveKeys(['web', 'api']);\n\n    expect($middleware['web'])\n        ->toContain(AuthenticatorMiddleware::class);\n\n    expect($middleware['api'])\n        ->toContain(AuthorizerMiddleware::class);\n});\n\ntest('auth0.registerAuthenticationRoutes === true registers routes', function (): void {\n    config(['auth0.registerAuthenticationRoutes' => true]);\n\n    $service = app(ServiceProvider::class, ['app' => $this->app]);\n    /**\n     * @var ServiceProvider $service\n     */\n    $service->registerRoutes();\n    $routes = (array) Route::getRoutes()->get('GET');\n\n    expect($routes)\n        ->toHaveKeys(['login', 'logout', 'callback']);\n});\n"
  },
  {
    "path": "tests/Unit/ServiceTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Service;\nuse Auth0\\Laravel\\Bridges\\{CacheBridge, CacheBridgeContract, SessionBridgeContract};\nuse Auth0\\SDK\\Contract\\Auth0Interface as SdkContract;\nuse Auth0\\SDK\\Auth0 as SDKAuth0;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Contract\\API\\ManagementInterface;\nuse Auth0\\SDK\\Store\\MemoryStore;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Support\\Facades\\Route;\nuse Psr\\Cache\\CacheItemPoolInterface;\nuse PsrMock\\Psr17\\ResponseFactory;\n\nuses()->group('Service');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n    $this->config = $this->sdk->configuration();\n    $this->session = $this->config->getSessionStorage();\n});\n\nit('returns a Management API class', function (): void {\n    expect($this->laravel->management())->toBeInstanceOf(ManagementInterface::class);\n});\n\nit('can get/set the configuration', function (): void {\n    expect($this->laravel->getConfiguration())->toBeInstanceOf(SdkConfiguration::class);\n\n    $configuration = new SdkConfiguration(['strategy' => 'none', 'domain' => uniqid() . '.auth0.test']);\n    $this->laravel->setConfiguration($configuration);\n    expect($this->laravel->getConfiguration())->toBe($configuration);\n\n    $domain = uniqid() . '.auth0.test';\n    $configuration->setDomain($domain);\n    expect($this->laravel->getConfiguration()->getDomain())->toBe($domain);\n\n    $configuration = new SdkConfiguration(['strategy' => 'none', 'domain' => uniqid() . '.auth0.test']);\n    $this->laravel->setConfiguration($configuration);\n    expect($this->laravel->getConfiguration())->toBe($configuration);\n\n    $sdk = $this->laravel->getSdk();\n    $configuration = new SdkConfiguration(['strategy' => 'none', 'domain' => uniqid() . '.auth0.test']);\n    $this->laravel->setConfiguration($configuration);\n    expect($this->laravel->getConfiguration())->toBe($configuration);\n    expect($sdk->configuration())->toBe($configuration);\n});\n\nit('can get the sdk credentials', function (): void {\n    expect($this->laravel->getCredentials())\n        ->toBeNull();\n\n    $this->session->set('user', ['sub' => 'hello|world']);\n    $this->session->set('idToken', (string) Generator::create((createRsaKeys())->private));\n    $this->session->set('accessToken', (string) Generator::create((createRsaKeys())->private));\n    $this->session->set('accessTokenScope', [uniqid()]);\n    $this->session->set('accessTokenExpiration', time() - 1000);\n\n    // As we manually set the session values, we need to refresh the SDK state to ensure it's in sync.\n    $this->sdk->refreshState();\n\n    expect($this->laravel->getCredentials())\n        ->toBeObject()\n        ->toHaveProperty('accessToken', $this->session->get('accessToken'))\n        ->toHaveProperty('accessTokenScope', $this->session->get('accessTokenScope'))\n        ->toHaveProperty('accessTokenExpiration', $this->session->get('accessTokenExpiration'))\n        ->toHaveProperty('idToken', $this->session->get('idToken'))\n        ->toHaveProperty('user', $this->session->get('user'));\n});\n\nit('can get/set the SDK', function (): void {\n    expect($this->laravel->getSdk())->toBeInstanceOf(SdkContract::class);\n\n    $sdk = new SDKAuth0(['strategy' => 'none']);\n    $this->laravel->setSdk($sdk);\n    expect($this->laravel->getSdk())->toBeInstanceOf(SdkContract::class);\n});\n\nit('can reset the internal static state', function (): void {\n    $cache = spl_object_id($this->laravel->getSdk());\n\n    unset($this->laravel); // Force the object to be destroyed. Static state will remain.\n\n    $laravel = app('auth0');\n    $updated = spl_object_id($laravel->getSdk());\n    expect($cache)->toBe($updated);\n\n    $laravel->reset(); // Reset the static state.\n\n    $laravel = app('auth0');\n    $updated = spl_object_id($laravel->getSdk());\n    expect($cache)->not->toBe($updated);\n});\n\ntest('bootStrategy() rejects non-string values', function (): void {\n    $method = new ReflectionMethod(Service::class, 'bootStrategy');\n    $method->setAccessible(true);\n\n    expect($method->invoke($this->laravel, ['strategy' => 123]))\n        ->toMatchArray(['strategy' => SdkConfiguration::STRATEGY_REGULAR]);\n});\n\ntest('bootSessionStorage() behaves as expected', function (): void {\n    $method = new ReflectionMethod(Service::class, 'bootSessionStorage');\n    $method->setAccessible(true);\n\n    expect($method->invoke($this->laravel, []))\n        ->sessionStorage->toBeInstanceOf(SessionBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['sessionStorage' => null]))\n        ->sessionStorage->toBeInstanceOf(SessionBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['sessionStorage' => false]))\n        ->sessionStorage->toBeNull();\n\n    expect($method->invoke($this->laravel, ['sessionStorage' => CacheBridge::class]))\n        ->sessionStorage->toBeNull();\n\n    expect($method->invoke($this->laravel, ['sessionStorage' => MemoryStore::class]))\n        ->sessionStorage->toBeInstanceOf(MemoryStore::class);\n\n    $this->app->singleton('testStore', static fn (): MemoryStore => app(MemoryStore::class));\n\n    expect($method->invoke($this->laravel, ['sessionStorage' => 'testStore']))\n        ->sessionStorage->toBeInstanceOf(MemoryStore::class);\n});\n\ntest('bootTransientStorage() behaves as expected', function (): void {\n    $method = new ReflectionMethod(Service::class, 'bootTransientStorage');\n    $method->setAccessible(true);\n\n    expect($method->invoke($this->laravel, []))\n        ->transientStorage->toBeInstanceOf(SessionBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['transientStorage' => null]))\n        ->transientStorage->toBeInstanceOf(SessionBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['transientStorage' => false]))\n        ->transientStorage->toBeNull();\n\n    expect($method->invoke($this->laravel, ['transientStorage' => CacheBridge::class]))\n        ->transientStorage->toBeNull();\n\n    expect($method->invoke($this->laravel, ['transientStorage' => MemoryStore::class]))\n        ->transientStorage->toBeInstanceOf(MemoryStore::class);\n\n    $this->app->singleton('testStore', static fn (): MemoryStore => app(MemoryStore::class));\n\n    expect($method->invoke($this->laravel, ['transientStorage' => 'testStore']))\n        ->transientStorage->toBeInstanceOf(MemoryStore::class);\n});\n\ntest('bootTokenCache() behaves as expected', function (): void {\n    $method = new ReflectionMethod(Service::class, 'bootTokenCache');\n    $method->setAccessible(true);\n\n    expect($method->invoke($this->laravel, []))\n        ->tokenCache->toBeInstanceOf(CacheBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['tokenCache' => null]))\n        ->tokenCache->toBeInstanceOf(CacheBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['tokenCache' => CacheBridge::class]))\n        ->tokenCache->toBeInstanceOf(CacheBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['tokenCache' => false]))\n        ->tokenCache->toBeNull();\n\n    expect($method->invoke($this->laravel, ['tokenCache' => MemoryStore::class]))\n        ->tokenCache->toBeNull();\n\n    expect($method->invoke($this->laravel, ['tokenCache' => 'cache.psr6']))\n        ->tokenCache->toBeInstanceOf(CacheItemPoolInterface::class);\n});\n\ntest('bootBackchannelLogoutCache() behaves as expected', function (): void {\n    $method = new ReflectionMethod(Service::class, 'bootBackchannelLogoutCache');\n    $method->setAccessible(true);\n\n    expect($method->invoke($this->laravel, []))\n        ->backchannelLogoutCache->toBeInstanceOf(CacheBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['backchannelLogoutCache' => null]))\n        ->backchannelLogoutCache->toBeInstanceOf(CacheBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['backchannelLogoutCache' => CacheBridge::class]))\n        ->backchannelLogoutCache->toBeInstanceOf(CacheBridgeContract::class);\n\n    expect($method->invoke($this->laravel, ['backchannelLogoutCache' => false]))\n        ->backchannelLogoutCache->toBeNull();\n\n    expect($method->invoke($this->laravel, ['backchannelLogoutCache' => MemoryStore::class]))\n        ->backchannelLogoutCache->toBeNull();\n\n    expect($method->invoke($this->laravel, ['backchannelLogoutCache' => 'cache.psr6']))\n        ->backchannelLogoutCache->toBeInstanceOf(CacheItemPoolInterface::class);\n});\n\n// test('bootManagementTokenCache() behaves as expected', function (): void {\n//     $method = new ReflectionMethod(Service::class, 'bootManagementTokenCache');\n//     $method->setAccessible(true);\n\n//     expect($method->invoke($this->laravel, []))\n//         ->managementTokenCache->toBeInstanceOf(CacheBridgeContract::class);\n\n//     expect($method->invoke($this->laravel, ['managementTokenCache' => null]))\n//         ->managementTokenCache->toBeInstanceOf(CacheBridgeContract::class);\n\n//     expect($method->invoke($this->laravel, ['managementTokenCache' => CacheBridgeContract::class]))\n//         ->managementTokenCache->toBeInstanceOf(CacheBridgeContract::class);\n\n//     expect($method->invoke($this->laravel, ['managementTokenCache' => false]))\n//         ->managementTokenCache->toBeNull();\n\n//     expect($method->invoke($this->laravel, ['managementTokenCache' => MemoryStore::class]))\n//         ->managementTokenCache->toBeNull();\n\n//     expect($method->invoke($this->laravel, ['managementTokenCache' => 'cache.psr6']))\n//         ->managementTokenCache->toBeInstanceOf(CacheItemPoolInterface::class);\n// });\n\ntest('json() behaves as expected', function (): void {\n    $factory = new ResponseFactory;\n\n    $response = $factory->createResponse(200);\n    $response->getBody()->write('{\"foo\":\"bar\"}');\n\n    expect(Service::json($response))\n        ->toBe(['foo' => 'bar']);\n\n    $response = $factory->createResponse(500);\n    $response->getBody()->write('{\"foo\":\"bar\"}');\n\n    expect(Service::json($response))\n        ->toBeNull();\n\n    $response = $factory->createResponse(200);\n    $response->getBody()->write(json_encode(true));\n\n    expect(Service::json($response))\n        ->toBeNull();\n});\n\ntest('routes() behaves as expected', function (): void {\n    Service::routes();\n\n    expect((array) Route::getRoutes()->get('GET'))\n        ->toHaveKeys(['login', 'logout', 'callback']);\n});\n"
  },
  {
    "path": "tests/Unit/Traits/ActingAsAuth0UserTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\Traits\\ActingAsAuth0User;\nuse Auth0\\Laravel\\Users\\UserContract;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('trait', 'impersonation', 'acting-as-auth0-user');\n\nuses(ActingAsAuth0User::class);\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n    $this->user = ['sub' => uniqid(), 'scope' => 'openid profile email read:messages'];\n});\n\nit('impersonates with other guards', function (): void {\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null\n    ]);\n\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize')->get($route, function () use ($route): string {\n        return json_encode(['user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $this->actingAsAuth0User(attributes: $this->user, source: Guard::SOURCE_SESSION)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK);\n\n    expect(auth()->guard()->user())->not()->toBeNull();\n});\n\nit('impersonates a user against auth0.authenticate', function (): void {\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate')->get($route, function () use ($route) {\n        return response()->json([\n            'user' => auth()->user(),\n            'status' => $route\n        ]);\n    });\n\n    $this->actingAsAuth0User(attributes: $this->user, source: Guard::SOURCE_SESSION)\n         ->withoutExceptionHandling()\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertJson(['status' => $route])\n         ->assertJsonFragment(['sub' => $this->user['sub']]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('impersonates a user against auth0.authenticate.optional', function (): void {\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate.optional')->get($route, function () use ($route) {\n        return response()->json([\n            'user' => auth()->user(),\n            'status' => $route\n        ]);\n    });\n\n    $this->actingAsAuth0User(attributes: $this->user, source: Guard::SOURCE_SESSION)\n        ->getJson($route)\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route])\n        ->assertJsonFragment(['sub' => $this->user['sub']]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('impersonates a user against auth0.authenticate using a scope', function (): void {\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate:read:messages')->get($route, function () use ($route) {\n        return response()->json([\n            'user' => auth()->user(),\n            'status' => $route\n        ]);\n    });\n\n    $this->actingAsAuth0User(attributes: $this->user, source: Guard::SOURCE_SESSION)\n        ->getJson($route)\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route])\n        ->assertJsonFragment(['sub' => $this->user['sub']]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('impersonates a user against auth0.authorize', function (): void {\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.audience' => [uniqid()],\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize')->get($route, function () use ($route) {\n        return response()->json([\n            'user' => auth()->user(),\n            'status' => $route\n        ]);\n    });\n\n    $this->actingAsAuth0User(attributes: $this->user, source: Guard::SOURCE_TOKEN)\n        ->getJson($route)\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route])\n        ->assertJsonFragment(['sub' => $this->user['sub']]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('impersonates a user against auth0.authorize.optional', function (): void {\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.audience' => [uniqid()],\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize.optional')->get($route, function () use ($route) {\n        return response()->json([\n            'user' => auth()->user(),\n            'status' => $route\n        ]);\n    });\n\n    $this->actingAsAuth0User(attributes: $this->user, source: Guard::SOURCE_TOKEN)\n        ->getJson($route)\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route])\n        ->assertJsonFragment(['sub' => $this->user['sub']]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n\nit('impersonates a user against auth0.authorize using a scope', function (): void {\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.audience' => [uniqid()],\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize:read:messages')->get($route, function () use ($route) {\n        return response()->json([\n            'user' => auth()->user(),\n            'status' => $route\n        ]);\n    });\n\n    $this->actingAsAuth0User(attributes: $this->user, source: Guard::SOURCE_TOKEN)\n        ->getJson($route)\n        ->assertStatus(Response::HTTP_OK)\n        ->assertJson(['status' => $route])\n        ->assertJsonFragment(['sub' => $this->user['sub']]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(UserContract::class);\n});\n"
  },
  {
    "path": "tests/Unit/Traits/ImpersonateTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Auth\\Guard;\nuse Auth0\\Laravel\\Guards\\AuthenticationGuard;\nuse Auth0\\Laravel\\Guards\\AuthorizationGuard;\nuse Auth0\\Laravel\\Entities\\CredentialEntity;\nuse Auth0\\Laravel\\Traits\\Impersonate;\nuse Auth0\\Laravel\\Users\\ImposterUser;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Token;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Route;\nuse PsrMock\\Psr18\\Client as MockHttpClient;\n\nuses()->group('trait', 'impersonate');\n\nuses(Impersonate::class);\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_API,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.audience' => [uniqid()],\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n        'auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256,\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n});\n\nit('impersonates with other guards', function (): void {\n    config([\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null\n    ]);\n\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize')->get($route, function () use ($route): string {\n        return json_encode(['user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = CredentialEntity::create(\n        user: new ImposterUser(['sub' => uniqid()]),\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->impersonate($imposter)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK);\n\n    expect($this->guard)\n        ->user()->toBeNull();\n});\n\nit('impersonates a user against auth0.authenticate', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate')->get($route, function () use ($route): string {\n        return json_encode(['user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = CredentialEntity::create(\n        user: new ImposterUser(['sub' => uniqid()]),\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->impersonate($imposter, Guard::SOURCE_SESSION)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertJson(['status' => $route])\n         ->assertJson(['user' => json_encode($imposter->getUser())]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(ImposterUser::class)\n        ->toBe($imposter->getUser());\n});\n\nit('impersonates a user against auth0.authenticate.optional', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate.optional')->get($route, function () use ($route): string {\n        return json_encode(['user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = CredentialEntity::create(\n        user: new ImposterUser(['sub' => uniqid()]),\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->impersonate($imposter, Guard::SOURCE_SESSION)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertJson(['status' => $route])\n         ->assertJson(['user' => json_encode($imposter->getUser())]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(ImposterUser::class)\n        ->toBe($imposter->getUser());\n});\n\nit('impersonates a user against auth0.authenticate using a scope', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authenticate:read:messages')->get($route, function () use ($route): string {\n        return json_encode(['user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = CredentialEntity::create(\n        user: new ImposterUser(['sub' => uniqid()]),\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->impersonate($imposter, Guard::SOURCE_SESSION)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertJson(['status' => $route])\n         ->assertJson(['user' => json_encode($imposter->getUser())]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(ImposterUser::class)\n        ->toBe($imposter->getUser());\n});\n\nit('impersonates a user against auth0.authorize', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize')->get($route, function () use ($route): string {\n        return json_encode(['user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = CredentialEntity::create(\n        user: new ImposterUser(['sub' => uniqid()]),\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->impersonate($imposter, Guard::SOURCE_TOKEN)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertJson(['status' => $route])\n         ->assertJson(['user' => json_encode($imposter->getUser())]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(ImposterUser::class)\n        ->toBe($imposter->getUser());\n});\n\nit('impersonates a user against auth0.authorize.optional', function (): void {\n    $route = '/' . uniqid();\n\n    Route::middleware('auth0.authorize.optional')->get($route, function () use ($route): string {\n        return json_encode(['user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = CredentialEntity::create(\n        user: new ImposterUser(['sub' => uniqid()]),\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    $this->impersonate($imposter, Guard::SOURCE_TOKEN)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertJson(['status' => $route])\n         ->assertJson(['user' => json_encode($imposter->getUser())]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(ImposterUser::class)\n        ->toBe($imposter->getUser());\n});\n\nit('impersonates a user against auth0.authorize using a scope', function (): void {\n    $route = '/' . uniqid();\n\n    $imposter = CredentialEntity::create(\n        user: new ImposterUser(['sub' => uniqid()]),\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600\n    );\n\n    Route::middleware('auth0.authorize:read:messages')->get($route, function () use ($route): string {\n        return json_encode(['user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $this->impersonate($imposter, Guard::SOURCE_TOKEN)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK)\n         ->assertJson(['status' => $route])\n         ->assertJson(['user' => json_encode($imposter->getUser())]);\n\n    expect($this->guard)\n        ->user()->toBeInstanceOf(ImposterUser::class)\n        ->toBe($imposter->getUser());\n});\n\nit('AuthenticationGuard returns the impersonated user', function (): void {\n    config([\n        'auth.defaults.guard' => 'auth0-session',\n    ]);\n\n    $route = '/' . uniqid();\n\n    Route::get($route, function () use ($route): string {\n        return json_encode(['route' => get_class(auth()->guard()), 'user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = new ImposterUser(['sub' => uniqid()]);\n\n    $credential = CredentialEntity::create(\n        user: $imposter,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600,\n        refreshToken: uniqid(),\n    );\n\n    $response = $this->impersonate($credential)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK);\n\n    expect($response->json())\n        ->route->toBe(AuthenticationGuard::class)\n        ->user->json()->sub->toBe($imposter->getAuthIdentifier());\n\n    expect(auth('legacyGuard'))\n        ->user()->toBeNull();\n\n    expect(auth('auth0-api'))\n        ->user()->toBeNull();\n\n    expect(auth('auth0-session'))\n        ->isImpersonating()->toBeTrue()\n        ->user()->toEqual($imposter)\n        ->find()->toEqual($credential)\n        ->findSession()->toEqual($credential)\n        ->getCredential()->toEqual($credential);\n\n    $client = new MockHttpClient(requestLimit: 0);\n    $this->sdk->configuration()->setHttpClient($client);\n\n    expect(auth('auth0-session'))\n        ->refreshUser();\n\n    auth('auth0-session')->setUser(new ImposterUser(['sub' => uniqid()]));\n\n    expect(auth('auth0-session'))\n        ->isImpersonating()->toBeFalse()\n        ->user()->not()->toEqual($imposter)\n        ->find()->not()->toEqual($credential)\n        ->findSession()->not()->toEqual($credential)\n        ->getCredential()->not()->toEqual($credential);\n});\n\nit('AuthorizationGuard returns the impersonated user', function (): void {\n    config([\n        'auth.defaults.guard' => 'auth0-api',\n    ]);\n\n    $route = '/' . uniqid();\n\n    Route::get($route, function () use ($route): string {\n        return json_encode(['route' => get_class(auth()->guard()), 'user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = new ImposterUser(['sub' => uniqid()]);\n\n    $credential = CredentialEntity::create(\n        user: $imposter,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600,\n        refreshToken: uniqid(),\n    );\n\n    $response = $this->impersonate($credential)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK);\n\n    expect($response->json())\n        ->route->toBe(AuthorizationGuard::class)\n        ->user->json()->sub->toBe($imposter->getAuthIdentifier());\n\n    expect(auth('legacyGuard'))\n        ->user()->toBeNull();\n\n    expect(auth('auth0-session'))\n        ->user()->toBeNull();\n\n    expect(auth('auth0-api'))\n        ->isImpersonating()->toBeTrue()\n        ->getImposterSource()->toBe(Guard::SOURCE_TOKEN)\n        ->user()->toEqual($imposter)\n        ->find()->toEqual($credential)\n        ->findToken()->toEqual($credential)\n        ->getCredential()->toEqual($credential);\n\n    $client = new MockHttpClient(requestLimit: 0);\n    $this->sdk->configuration()->setHttpClient($client);\n\n    expect(auth('auth0-api'))\n        ->refreshUser();\n\n    auth('auth0-api')->setUser(new ImposterUser(['sub' => uniqid()]));\n\n    expect(auth('auth0-api'))\n        ->isImpersonating()->toBeFalse()\n        ->user()->not()->toEqual($imposter)\n        ->find()->not()->toEqual($credential)\n        ->findToken()->not()->toEqual($credential)\n        ->getCredential()->not()->toEqual($credential);\n});\n\nit('Guard returns the impersonated user', function (): void {\n    config([\n        'auth.defaults.guard' => 'legacyGuard',\n    ]);\n\n    $route = '/' . uniqid();\n\n    Route::get($route, function () use ($route): string {\n        return json_encode(['route' => get_class(auth()->guard()), 'user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = new ImposterUser(['sub' => uniqid()]);\n\n    $credential = CredentialEntity::create(\n        user: $imposter,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600,\n        refreshToken: uniqid(),\n    );\n\n    $response = $this->impersonate(credential: $credential, source: Guard::SOURCE_SESSION)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK);\n\n    expect($response->json())\n        ->route->toBe(Guard::class)\n        ->user->json()->sub->toBe($imposter->getAuthIdentifier());\n\n    expect(auth('auth0-api'))\n        ->user()->toBeNull();\n\n    expect(auth('auth0-session'))\n        ->user()->toBeNull();\n\n    expect(auth('legacyGuard'))\n        ->isImpersonating()->toBeTrue()\n        ->getImposterSource()->toBe(Guard::SOURCE_SESSION)\n        ->user()->toEqual($imposter)\n        ->find()->toEqual($credential)\n        ->getCredential()->toEqual($credential);\n\n    $client = new MockHttpClient(requestLimit: 0);\n    $this->sdk->configuration()->setHttpClient($client);\n\n    auth('legacyGuard')->refreshUser();\n    auth('legacyGuard')->setUser(new ImposterUser(['sub' => uniqid()]));\n\n    expect(auth('legacyGuard'))\n        ->isImpersonating()->toBeFalse()\n        ->user()->not()->toEqual($imposter)\n        ->find()->not()->toEqual($credential)\n        ->getCredential()->not()->toEqual($credential);\n});\n\nit('Guard clears the impersonated user during logout()', function (): void {\n    config([\n        'auth.defaults.guard' => 'legacyGuard',\n    ]);\n\n    $route = '/' . uniqid();\n\n    Route::get($route, function () use ($route): string {\n        return json_encode(['route' => get_class(auth()->guard()), 'user' => json_encode(auth()->user()), 'status' => $route]);\n    });\n\n    $imposter = new ImposterUser(['sub' => uniqid()]);\n\n    $credential = CredentialEntity::create(\n        user: $imposter,\n        idToken: mockIdToken(algorithm: Token::ALGO_HS256),\n        accessToken: mockAccessToken(algorithm: Token::ALGO_HS256),\n        accessTokenScope: ['openid', 'profile', 'email', 'read:messages'],\n        accessTokenExpiration: time() + 3600,\n        refreshToken: uniqid(),\n    );\n\n    $response = $this->impersonate(credential: $credential, source: Guard::SOURCE_TOKEN)\n         ->getJson($route)\n         ->assertStatus(Response::HTTP_OK);\n\n    expect($response->json())\n        ->route->toBe(Guard::class)\n        ->user->json()->sub->toBe($imposter->getAuthIdentifier());\n\n    expect(auth('legacyGuard'))\n        ->isImpersonating()->toBeTrue()\n        ->getImposterSource()->toBe(Guard::SOURCE_TOKEN)\n        ->user()->toEqual($imposter)\n        ->find()->toEqual($credential)\n        ->getCredential()->toEqual($credential);\n\n    auth('legacyGuard')->logout();\n\n    expect(auth('legacyGuard'))\n        ->isImpersonating()->toBeFalse()\n        ->user()->toBeNull()\n        ->find()->toBeNull()\n        ->getCredential()->toBeNull();\n});\n"
  },
  {
    "path": "tests/Unit/UserProviderTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Guards\\AuthorizationGuard;\nuse Auth0\\Laravel\\UserProvider;\nuse Auth0\\Laravel\\UserRepository;\nuse Auth0\\Laravel\\Users\\StatefulUser;\nuse Auth0\\Laravel\\Users\\StatefulUserContract;\nuse Auth0\\SDK\\Configuration\\SdkConfiguration;\nuse Auth0\\SDK\\Exception\\InvalidTokenException;\nuse Auth0\\SDK\\Token;\nuse Auth0\\SDK\\Token\\Generator;\nuse Illuminate\\Contracts\\Container\\BindingResolutionException;\nuse Illuminate\\Http\\Response;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Support\\Facades\\Route;\n\nuses()->group('UserProvider');\n\nbeforeEach(function (): void {\n    $this->secret = uniqid();\n\n    config([\n        'auth0.AUTH0_CONFIG_VERSION' => 2,\n        'auth0.guards.default.strategy' => SdkConfiguration::STRATEGY_REGULAR,\n        'auth0.guards.default.domain' => uniqid() . '.auth0.com',\n        'auth0.guards.default.clientId' => uniqid(),\n        'auth0.guards.default.clientSecret' => $this->secret,\n        'auth0.guards.default.cookieSecret' => uniqid(),\n    ]);\n\n    $this->laravel = app('auth0');\n    $this->guard = auth('legacyGuard');\n    $this->sdk = $this->laravel->getSdk();\n});\n\ntest('retrieveByToken() returns null when an incompatible guard token is used', function (): void {\n    config([\n        'auth.defaults.guard' => 'web',\n        'auth.guards.legacyGuard' => null\n    ]);\n\n    $route = '/' . uniqid();\n    Route::get($route, function () {\n        $provider = Auth::createUserProvider('auth0-provider');\n        $credential = $provider->retrieveByToken('token', '');\n\n        if (null === $credential) {\n            return response()->json(['status' => 'OK']);\n        }\n\n        abort(Response::HTTP_UNAUTHORIZED, 'Unauthorized');\n    });\n\n    $this->getJson($route)\n        ->assertOK();\n});\n\ntest('retrieveByToken() returns null when an invalid token is provided', function (): void {\n    config(['auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256]);\n\n    $token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://123.' . config('auth0.guards.default.domain') . '/',\n        'sub' => 'hello|world',\n        'aud' => config('auth0.guards.default.clientId'),\n        'iat' => time(),\n        'exp' => time() + 60,\n        'azp' => config('auth0.guards.default.clientId'),\n        'scope' => 'openid profile email'\n    ], []);\n\n    $route = '/' . uniqid();\n    Route::get($route, function () use ($token) {\n        $provider = Auth::createUserProvider('auth0-provider');\n        $credential = $provider->retrieveByToken('token', $token);\n\n        if (null === $credential) {\n            return response()->json(['status' => 'OK']);\n        }\n\n        abort(Response::HTTP_UNAUTHORIZED, 'Unauthorized');\n    });\n\n\n    $this->getJson($route)\n        ->assertOK();\n});\n\ntest('retrieveByToken() returns a user when a valid token is provided', function (): void {\n    config(['auth0.guards.default.tokenAlgorithm' => Token::ALGO_HS256]);\n\n    $token = Generator::create($this->secret, Token::ALGO_HS256, [\n        \"iss\" => 'https://' . config('auth0.guards.default.domain') . '/',\n        'sub' => 'hello|world',\n        'aud' => config('auth0.guards.default.clientId'),\n        'iat' => time(),\n        'exp' => time() + 60,\n        'azp' => config('auth0.guards.default.clientId'),\n        'scope' => 'openid profile email'\n    ], []);\n\n    $route = '/' . uniqid();\n    Route::get($route, function () use ($token) {\n        $provider = Auth::createUserProvider('auth0-provider');\n        $credential = $provider->retrieveByToken('token', (string) $token);\n\n        if (null !== $credential) {\n            return response()->json(['status' => 'OK']);\n        }\n\n        abort(Response::HTTP_UNAUTHORIZED, 'Unauthorized');\n    });\n\n    $this->getJson($route)\n         ->assertOK();\n});\n\ntest('validateCredentials() always returns false', function (): void {\n    $provider = Auth::createUserProvider('auth0-provider');\n    $user = new StatefulUser();\n\n    expect($provider->validateCredentials($user, []))\n        ->toBeFalse();\n});\n\ntest('getRepository() throws an error when an non-existent repository provider is set', function (): void {\n    $provider = new UserProvider(['model' => 'MISSING']);\n    $provider->getRepository();\n})->throws(BindingResolutionException::class);\n\ntest('getRepository() throws an error when an invalid repository provider is set', function (): void {\n    $provider = new UserProvider(['model' => ['ARRAY']]);\n    $provider->getRepository();\n})->throws(BindingResolutionException::class);\n\ntest('setRepository() sets the repository model', function (): void {\n    $provider = new UserProvider(['model' => uniqid()]);\n    $repository = new UserRepository();\n    $provider->setRepository($repository::class);\n\n    expect($provider->getRepository())\n        ->toBeInstanceOf($repository::class);\n});\n\ntest('setRepository() with the same repository identifier uses the cached repository instance', function (): void {\n    $provider = new UserProvider(['model' => 'MISSING']);\n    $repository = new UserRepository();\n\n    $provider->setRepository($repository::class);\n\n    expect($provider->getRepository())\n        ->toBeInstanceOf($repository::class);\n\n    $provider->setRepository($repository::class);\n\n    expect($provider->getRepository())\n        ->toBeInstanceOf($repository::class);\n});\n\ntest('retrieveByCredentials() returns `null` when an empty array is provided', function (): void {\n    $provider = new UserProvider(['model' => uniqid()]);\n    $repository = new UserRepository();\n\n    expect($provider->retrieveByCredentials([]))->toBeNull();\n});\n"
  },
  {
    "path": "tests/Unit/UserRepositoryTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Users\\StatefulUser;\nuse Auth0\\Laravel\\Users\\StatelessUser;\n\nuses()->group('UserRepository');\n\nit('returns a stateful user model from session queries', function (): void {\n    $repository = $this->app['auth0.repository'];\n\n    expect($repository->fromSession(['name' => 'Stateful']))\n        ->toBeInstanceOf(StatefulUser::class)\n        ->name->toBe('Stateful');\n});\n\nit('returns a stateless user model from access token queries', function (): void {\n    $repository = $this->app['auth0.repository'];\n\n    expect($repository->fromAccessToken(['name' => 'Stateless']))\n        ->toBeInstanceOf(StatelessUser::class)\n        ->name->toBe('Stateless');\n});\n"
  },
  {
    "path": "tests/Unit/Users/UserTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nuse Auth0\\Laravel\\Users\\ImposterUser;\n\nuses()->group('stateful', 'model', 'model.user');\n\nit('fills attributes provided to the constructor', function (): void {\n    $user = new ImposterUser(['testing' => 'testing']);\n\n    expect($user->testing)\n        ->toBe('testing');\n});\n\nit('fills attributes', function (): void {\n    $user = new ImposterUser();\n    $user->fill(['testing' => 'testing']);\n\n    expect($user->testing)\n        ->toBe('testing');\n});\n\nit('sets attributes with magic', function (): void {\n    $user = new ImposterUser();\n    $user->testing = 'testing';\n\n    expect($user->testing)\n        ->toBe('testing');\n});\n\nit('sets attributes', function (): void {\n    $user = new ImposterUser();\n    $user->setAttribute('testing', 'testing');\n\n    expect($user->getAttribute('testing'))\n        ->toBe('testing');\n});\n\nit('gets attributes array', function (): void {\n    $user = new ImposterUser([\n        'testing' => 'testing',\n        'testing2' => 'testing2',\n    ]);\n\n    expect($user->getAttributes())\n        ->toBeArray()\n        ->toContain('testing')\n        ->toContain('testing2');\n});\n\nit('supports getting the identifier', function (): void {\n    $user = new ImposterUser(['sub' => 'testing']);\n\n    expect($user->getAuthIdentifier())\n        ->toBe('testing');\n});\n\nit('supports getting the identifier name', function (): void {\n    $user = new ImposterUser(['sub' => 'testing']);\n\n    expect($user->getAuthIdentifierName())\n        ->toBe('id');\n});\n\nit('supports getting the password', function (): void {\n    $user = new ImposterUser();\n\n    expect($user->getAuthPassword())\n        ->toBe('');\n});\n\nit('supports getting the password name', function (): void {\n    $user = new ImposterUser();\n\n    expect($user->getAuthPasswordName())\n        ->toBe('password');\n});\n\nit('supports getting the remember token', function (): void {\n    $user = new ImposterUser();\n\n    expect($user->getRememberToken())\n        ->toBe('');\n});\n\nit('supports getting the remember token name', function (): void {\n    $user = new ImposterUser();\n\n    expect($user->getRememberTokenName())\n        ->toBe('');\n});\n\nit('supports setting the remember token', function (): void {\n    $user = new ImposterUser();\n\n    expect($user->setRememberToken('testing'))\n        ->toBeNull();\n});\n\nit('supports JSON serialization', function (): void {\n    $user = new ImposterUser(['testing' => 'testing']);\n\n    expect($user->jsonSerialize())\n        ->toBeArray()\n        ->toContain('testing');\n\n    expect(json_encode($user))\n        ->toBeJson('{\"testing\":\"testing\"}');\n});\n"
  }
]