├── .editorconfig ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── Bug Report.yml │ ├── Feature Request.yml │ └── config.yml └── workflows │ ├── cron_snyk.yml │ ├── pr_await_changes.yml │ ├── pr_composer.yml │ ├── pr_pest.yml │ ├── pr_phpcsf.yml │ ├── pr_phpstan.yml │ ├── pr_psalm.yml │ ├── pr_rector.yml │ ├── pr_snyk.yml │ └── semgrep.yml ├── .gitignore ├── LICENSE ├── README.md └── app ├── .editorconfig ├── .env.example ├── .php-cs-fixer.dist.php ├── .phpcs.xml.dist ├── README.md ├── composer.json ├── phpstan.neon.dist ├── phpunit.xml ├── psalm.xml.dist ├── public ├── bootstrap.php └── index.php ├── rector.php ├── src ├── Application.php ├── ApplicationErrorHandler.php ├── ApplicationRouter.php ├── ApplicationTemplates.php └── Contract │ └── QuickstartExample.php ├── templates ├── _json.php ├── error.php ├── logged-in.php ├── logged-out.php └── spa.php └── tests ├── Feature └── ExampleTest.php ├── Pest.php ├── TestCase.php └── Unit └── ExampleTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | # We use 4 space indentation for PHP 10 | [*.php] 11 | indent_style = space 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @auth0-samples/dx-sdks-engineer 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug Report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Report a bug 2 | description: Have you found a bug or issue? Create a bug report for this sample 3 | 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | **Please do not report security vulnerabilities here**. The [Responsible Disclosure Program](https://auth0.com/responsible-disclosure-policy) details the procedure for disclosing security issues. 9 | 10 | - type: checkboxes 11 | id: checklist 12 | attributes: 13 | label: Checklist 14 | options: 15 | - label: I have looked into the [Readme](https://github.com/auth0-samples/auth0-php-api-samples#readme) and have not found a suitable solution or answer. 16 | required: true 17 | - label: I have searched the [issues](https://github.com/auth0-samples/auth0-php-api-samples/issues) and have not found a suitable solution or answer. 18 | required: true 19 | - label: I have searched the [Auth0 Community](https://community.auth0.com) forums and have not found a suitable solution or answer. 20 | required: true 21 | - label: I agree to the terms within the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md). 22 | required: true 23 | 24 | - type: textarea 25 | id: description 26 | attributes: 27 | label: Description 28 | description: Provide a clear and concise description of the issue, including what you expected to happen. 29 | validations: 30 | required: true 31 | 32 | - type: textarea 33 | id: reproduction 34 | attributes: 35 | label: Reproduction 36 | description: Detail the steps taken to reproduce this error, and whether this issue can be reproduced consistently or if it is intermittent. 37 | placeholder: | 38 | 1. Step 1... 39 | 2. Step 2... 40 | 3. ... 41 | validations: 42 | required: true 43 | 44 | - type: textarea 45 | id: additional-context 46 | attributes: 47 | label: Additional context 48 | description: Any other relevant information you think would be useful. 49 | validations: 50 | required: false 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature Request.yml: -------------------------------------------------------------------------------- 1 | name: 🧩 Feature request 2 | description: Suggest an idea or a feature for this sample 3 | labels: ["feature request"] 4 | 5 | body: 6 | - type: checkboxes 7 | id: checklist 8 | attributes: 9 | label: Checklist 10 | options: 11 | - label: I have looked into the [Readme](https://github.com/auth0-samples/auth0-php-api-samples#readme) and have not found a suitable solution or answer. 12 | required: true 13 | - label: I have searched the [issues](https://github.com/auth0-samples/auth0-php-api-samples/issues) and have not found a suitable solution or answer. 14 | required: true 15 | - label: I have searched the [Auth0 Community](https://community.auth0.com) forums and have not found a suitable solution or answer. 16 | required: true 17 | - label: I agree to the terms within the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md). 18 | required: true 19 | 20 | - type: textarea 21 | id: description 22 | attributes: 23 | label: Describe the problem you'd like to have solved 24 | description: A clear and concise description of what the problem is. 25 | validations: 26 | required: true 27 | 28 | - type: textarea 29 | id: ideal-solution 30 | attributes: 31 | label: Describe the ideal solution 32 | description: A clear and concise description of what you want to happen. 33 | validations: 34 | required: true 35 | 36 | - type: textarea 37 | id: alternatives-and-workarounds 38 | attributes: 39 | label: Alternatives and current workarounds 40 | description: A clear and concise description of any alternatives you've considered or any workarounds that are currently in place. 41 | validations: 42 | required: false 43 | 44 | - type: textarea 45 | id: additional-context 46 | attributes: 47 | label: Additional context 48 | description: Add any other context or screenshots about the feature request here. 49 | validations: 50 | required: false 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 🤔 Help & Questions 4 | url: https://community.auth0.com 5 | about: Ask general support or usage questions in the Auth0 Community forums. 6 | -------------------------------------------------------------------------------- /.github/workflows/cron_snyk.yml: -------------------------------------------------------------------------------- 1 | name: "Snyk (Scheduled)" 2 | 3 | # This workflow will run after a push to the main branch and as a scheduled job. 4 | 5 | on: 6 | push: 7 | branches: ["master", "main"] 8 | 9 | permissions: {} 10 | 11 | defaults: 12 | run: 13 | working-directory: app 14 | 15 | jobs: 16 | snyk: 17 | name: "Scan" 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: shivammathur/setup-php@v2 22 | with: 23 | php-version: "8.1" 24 | coverage: none 25 | extensions: mbstring 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | 29 | - uses: actions/checkout@v3 30 | 31 | - run: composer install --no-progress 32 | 33 | - uses: snyk/actions/php@master 34 | continue-on-error: true 35 | env: 36 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 37 | -------------------------------------------------------------------------------- /.github/workflows/pr_await_changes.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Changes" 2 | 3 | # Monitor for changes to pull requests. 4 | 5 | on: 6 | pull_request: 7 | types: [opened, synchronize, reopened, closed] 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | wait: 13 | name: "Watching" 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - run: echo "Child workflows triggered." 18 | -------------------------------------------------------------------------------- /.github/workflows/pr_composer.yml: -------------------------------------------------------------------------------- 1 | name: "Composer" 2 | 3 | on: 4 | pull_request: 5 | merge_group: 6 | push: 7 | branches: ["master", "main"] 8 | 9 | permissions: {} 10 | 11 | defaults: 12 | run: 13 | working-directory: app 14 | 15 | jobs: 16 | validate: 17 | name: "Validate" 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - run: composer validate 24 | 25 | normalize: 26 | name: "Normalize" 27 | runs-on: ubuntu-latest 28 | 29 | steps: 30 | - uses: actions/checkout@v3 31 | 32 | - run: composer require --dev ergebnis/composer-normalize 33 | 34 | - run: composer config allow-plugins.ergebnis/composer-normalize true 35 | 36 | - run: composer normalize 37 | -------------------------------------------------------------------------------- /.github/workflows/pr_pest.yml: -------------------------------------------------------------------------------- 1 | name: "PEST" 2 | 3 | on: 4 | pull_request: 5 | merge_group: 6 | push: 7 | branches: ["master", "main"] 8 | 9 | permissions: {} 10 | 11 | defaults: 12 | run: 13 | working-directory: app 14 | 15 | jobs: 16 | pest: 17 | name: "Scan" 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: "8.1" 26 | coverage: pcov 27 | 28 | - run: composer install --no-progress 29 | 30 | - run: vendor/bin/pest --order-by random --fail-on-risky --stop-on-defect --coverage --parallel 31 | 32 | - uses: codecov/codecov-action@v3 33 | with: 34 | directory: ./coverage/ 35 | flags: unittests 36 | -------------------------------------------------------------------------------- /.github/workflows/pr_phpcsf.yml: -------------------------------------------------------------------------------- 1 | name: "PHP CS Fixer" 2 | 3 | on: 4 | pull_request: 5 | merge_group: 6 | push: 7 | branches: ["master", "main"] 8 | 9 | permissions: {} 10 | 11 | defaults: 12 | run: 13 | working-directory: app 14 | 15 | jobs: 16 | phpcsf: 17 | name: "Scan" 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: "8.1" 26 | 27 | - run: composer install --no-progress 28 | 29 | - run: vendor/bin/php-cs-fixer fix src --dry-run --diff 30 | -------------------------------------------------------------------------------- /.github/workflows/pr_phpstan.yml: -------------------------------------------------------------------------------- 1 | name: "PHPStan" 2 | 3 | on: 4 | pull_request: 5 | merge_group: 6 | push: 7 | branches: ["master", "main"] 8 | 9 | permissions: {} 10 | 11 | defaults: 12 | run: 13 | working-directory: app 14 | 15 | jobs: 16 | phpstan: 17 | name: "Scan" 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: "8.1" 26 | 27 | - run: composer install --no-progress 28 | 29 | # - run: vendor/bin/phpstan analyze --no-ansi --no-progress --debug 30 | -------------------------------------------------------------------------------- /.github/workflows/pr_psalm.yml: -------------------------------------------------------------------------------- 1 | name: "Psalm" 2 | 3 | on: 4 | pull_request: 5 | merge_group: 6 | push: 7 | branches: ["master", "main"] 8 | 9 | permissions: {} 10 | 11 | defaults: 12 | run: 13 | working-directory: app 14 | 15 | jobs: 16 | psalm: 17 | name: "Scan" 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: "8.1" 26 | 27 | - run: composer install --no-progress 28 | 29 | # - run: vendor/bin/psalm 30 | -------------------------------------------------------------------------------- /.github/workflows/pr_rector.yml: -------------------------------------------------------------------------------- 1 | name: "Rector" 2 | 3 | on: 4 | pull_request: 5 | merge_group: 6 | push: 7 | branches: ["master", "main"] 8 | 9 | permissions: {} 10 | 11 | defaults: 12 | run: 13 | working-directory: app 14 | 15 | jobs: 16 | rector: 17 | name: "Scan" 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: "8.1" 26 | 27 | - run: composer install --no-progress 28 | 29 | - run: vendor/bin/rector process --dry-run 30 | -------------------------------------------------------------------------------- /.github/workflows/pr_snyk.yml: -------------------------------------------------------------------------------- 1 | name: "Snyk" 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Pull Request Changes"] 6 | types: 7 | - completed 8 | 9 | permissions: {} 10 | 11 | defaults: 12 | run: 13 | working-directory: app 14 | 15 | jobs: 16 | snyk: 17 | name: "Scan" 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: shivammathur/setup-php@v2 22 | with: 23 | php-version: "8.1" 24 | coverage: none 25 | extensions: mbstring 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | 29 | - uses: actions/checkout@v3 30 | with: 31 | ref: ${{ github.event.pull_request.head.sha }} 32 | github-token: ${{ secrets.GITHUB_TOKEN }} 33 | 34 | - run: composer install --no-progress 35 | 36 | - uses: snyk/actions/php@master 37 | continue-on-error: true 38 | env: 39 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 40 | -------------------------------------------------------------------------------- /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | name: Semgrep 2 | 3 | on: 4 | pull_request_target: {} 5 | push: 6 | branches: ["master", "main"] 7 | permissions: 8 | contents: read 9 | jobs: 10 | semgrep: 11 | name: Scan 12 | runs-on: ubuntu-latest 13 | container: 14 | image: returntocorp/semgrep 15 | if: (github.actor != 'dependabot[bot]' && github.actor != 'snyk-bot') 16 | steps: 17 | - uses: actions/checkout@v3 18 | - run: semgrep ci 19 | env: 20 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .env 3 | composer.lock 4 | .DS_Store 5 | app/.php-cs-fixer.cache 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Auth0 Samples 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Auth0 + PHP Backend API Sample 2 | 3 | This sample demonstrates how to add endpoint authorization to a [PHP](http://php.net/) backend API service using [Auth0](https://auth0.com). The project source code can be found in the [app directory](./app). Check the [PHP Quickstart](https://auth0.com/docs/quickstart/backend/php) to understand this sample better. 4 | 5 | ## Vulnerability Reporting 6 | 7 | Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 8 | 9 | ## What is Auth0? 10 | 11 | Auth0 helps you to easily: 12 | 13 | - implement authentication with multiple identity providers, including social (e.g., Google, Facebook, Microsoft, LinkedIn, GitHub, Twitter, etc), or enterprise (e.g., Windows Azure AD, Google Apps, Active Directory, ADFS, SAML, etc.) 14 | - log in users with username/password databases, passwordless, or multi-factor authentication 15 | - link multiple user accounts together 16 | - generate signed JSON Web Tokens to authorize your API calls and flow the user identity securely 17 | - access demographics and analytics detailing how, when, and where users are logging in 18 | - enrich user profiles from other data sources using customizable JavaScript rules 19 | 20 | [Why Auth0?](https://auth0.com/why-auth0) 21 | 22 | ## License 23 | 24 | This project is licensed under the MIT license. See the [LICENSE](https://github.com/auth0-samples/auth0-php-web-app/blob/master/LICENSE) file for more info. 25 | -------------------------------------------------------------------------------- /app/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /app/.env.example: -------------------------------------------------------------------------------- 1 | # --------- 2 | # Required: 3 | # --------- 4 | 5 | # Your Auth0 application's Client ID 6 | AUTH0_CLIENT_ID={CLIENT_ID} 7 | 8 | # The URL of your Auth0 tenant domain 9 | AUTH0_DOMAIN={DOMAIN} 10 | 11 | # Your Auth0 application's Client Secret 12 | AUTH0_CLIENT_SECRET={CLIENT_SECRET} 13 | 14 | # The Identifier from your custom Auth0 API. 15 | AUTH0_AUDIENCE={API_IDENTIFIER} 16 | 17 | # ------------------------------------------------------------------------- 18 | # Optional: Remove the leading # from the following options to enable them: 19 | # ------------------------------------------------------------------------- 20 | 21 | # An Organization Id for testing Organizations. 22 | # AUTH0_ORGANIZATION={ORGANIZATION_ID} 23 | -------------------------------------------------------------------------------- /app/.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | setRiskyAllowed(true) 7 | ->setRules([ 8 | 'array_indentation' => true, 9 | 'array_push' => true, 10 | 'array_syntax' => ['syntax' => 'short'], 11 | 'assign_null_coalescing_to_coalesce_equal' => true, 12 | 'backtick_to_shell_exec' => true, 13 | 'binary_operator_spaces' => true, 14 | 'blank_line_after_namespace' => true, 15 | 'blank_line_after_opening_tag' => true, 16 | 'blank_line_before_statement' => true, 17 | 'blank_line_between_import_groups' => true, 18 | 'braces' => true, 19 | 'cast_spaces' => true, 20 | 'class_attributes_separation' => ['elements' => ['const' => 'one', 'method' => 'one', 'property' => 'one', 'trait_import' => 'one', 'case' => 'one']], 21 | '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], 22 | 'class_reference_name_casing' => true, 23 | 'clean_namespace' => true, 24 | 'combine_consecutive_issets' => true, 25 | 'combine_consecutive_unsets' => true, 26 | 'combine_nested_dirname' => true, 27 | 'comment_to_phpdoc' => ['ignored_tags' => ['codeCoverageIgnoreStart', 'codeCoverageIgnoreEnd', 'phpstan-ignore-next-line']], 28 | 'compact_nullable_typehint' => true, 29 | 'concat_space' => ['spacing' => 'one'], 30 | 'constant_case' => ['case' => 'lower'], 31 | '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], 32 | 'date_time_create_from_format_call' => true, 33 | 'date_time_immutable' => true, 34 | 'declare_equal_normalize' => ['space' => 'none'], 35 | 'declare_parentheses' => true, 36 | 'declare_strict_types' => true, 37 | 'dir_constant' => true, 38 | 'doctrine_annotation_array_assignment' => true, 39 | 'doctrine_annotation_braces' => true, 40 | 'doctrine_annotation_indentation' => true, 41 | 'doctrine_annotation_spaces' => true, 42 | 'echo_tag_syntax' => ['format' => 'long'], 43 | 'elseif' => true, 44 | 'empty_loop_body' => true, 45 | 'empty_loop_condition' => true, 46 | 'encoding' => true, 47 | 'ereg_to_preg' => true, 48 | 'error_suppression' => true, 49 | 'escape_implicit_backslashes' => true, 50 | 'explicit_indirect_variable' => true, 51 | 'explicit_string_variable' => true, 52 | 'final_class' => true, 53 | 'final_internal_class' => true, 54 | 'final_public_method_for_abstract_class' => true, 55 | 'fopen_flag_order' => true, 56 | 'fopen_flags' => true, 57 | 'full_opening_tag' => true, 58 | 'fully_qualified_strict_types' => true, 59 | 'function_declaration' => true, 60 | 'function_to_constant' => true, 61 | 'function_typehint_space' => true, 62 | 'general_phpdoc_annotation_remove' => true, 63 | 'general_phpdoc_tag_rename' => true, 64 | 'get_class_to_class_keyword' => true, 65 | 'global_namespace_import' => ['import_classes' => true, 'import_constants' => true, 'import_functions' => true], 66 | 'group_import' => true, 67 | 'heredoc_indentation' => true, 68 | 'heredoc_to_nowdoc' => true, 69 | 'implode_call' => true, 70 | 'include' => true, 71 | 'increment_style' => ['style' => 'pre'], 72 | 'indentation_type' => true, 73 | 'integer_literal_case' => true, 74 | 'is_null' => true, 75 | 'lambda_not_used_import' => true, 76 | 'line_ending' => true, 77 | 'linebreak_after_opening_tag' => true, 78 | 'list_syntax' => ['syntax' => 'short'], 79 | 'logical_operators' => true, 80 | 'lowercase_cast' => true, 81 | 'lowercase_keywords' => true, 82 | 'lowercase_static_reference' => true, 83 | 'magic_constant_casing' => true, 84 | 'magic_method_casing' => true, 85 | 'mb_str_functions' => false, 86 | 'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline', 'after_heredoc' => true], 87 | 'method_chaining_indentation' => true, 88 | 'modernize_strpos' => true, 89 | 'modernize_types_casting' => true, 90 | 'multiline_comment_opening_closing' => true, 91 | 'multiline_whitespace_before_semicolons' => true, 92 | 'native_function_casing' => true, 93 | 'native_function_invocation' => true, 94 | 'native_function_type_declaration_casing' => true, 95 | 'new_with_braces' => true, 96 | 'no_alias_functions' => true, 97 | 'no_alias_language_construct_call' => true, 98 | 'no_alternative_syntax' => true, 99 | 'no_binary_string' => true, 100 | 'no_blank_lines_after_class_opening' => true, 101 | 'no_blank_lines_after_phpdoc' => true, 102 | 'no_break_comment' => true, 103 | 'no_closing_tag' => true, 104 | 'no_empty_comment' => true, 105 | 'no_empty_phpdoc' => true, 106 | 'no_empty_statement' => true, 107 | 'no_extra_blank_lines' => true, 108 | 'no_homoglyph_names' => true, 109 | 'no_leading_import_slash' => true, 110 | 'no_leading_namespace_whitespace' => true, 111 | 'no_mixed_echo_print' => true, 112 | 'no_multiline_whitespace_around_double_arrow' => true, 113 | 'no_multiple_statements_per_line' => true, 114 | 'no_php4_constructor' => true, 115 | 'no_short_bool_cast' => true, 116 | 'no_singleline_whitespace_before_semicolons' => true, 117 | 'no_space_around_double_colon' => true, 118 | 'no_spaces_after_function_name' => true, 119 | 'no_spaces_around_offset' => true, 120 | 'no_spaces_inside_parenthesis' => true, 121 | 'no_superfluous_elseif' => true, 122 | 'no_trailing_comma_in_singleline' => true, 123 | 'no_trailing_whitespace_in_comment' => true, 124 | 'no_trailing_whitespace_in_string' => true, 125 | 'no_trailing_whitespace' => true, 126 | 'no_unneeded_control_parentheses' => true, 127 | 'no_unneeded_curly_braces' => true, 128 | 'no_unneeded_final_method' => true, 129 | 'no_unneeded_import_alias' => true, 130 | 'no_unreachable_default_argument_value' => true, 131 | 'no_unset_cast' => true, 132 | 'no_unused_imports' => true, 133 | 'no_useless_concat_operator' => true, 134 | 'no_useless_else' => true, 135 | 'no_useless_nullsafe_operator' => true, 136 | 'no_useless_return' => true, 137 | 'no_useless_sprintf' => true, 138 | 'no_whitespace_before_comma_in_array' => true, 139 | 'no_whitespace_in_blank_line' => true, 140 | 'non_printable_character' => true, 141 | 'normalize_index_brace' => true, 142 | 'not_operator_with_successor_space' => true, 143 | 'nullable_type_declaration_for_default_null_value' => true, 144 | 'object_operator_without_whitespace' => true, 145 | 'octal_notation' => true, 146 | 'operator_linebreak' => true, 147 | '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']], 148 | 'ordered_imports' => ['sort_algorithm' => 'alpha', 'imports_order' => ['const', 'class', 'function']], 149 | 'ordered_interfaces' => true, 150 | 'ordered_traits' => true, 151 | 'php_unit_fqcn_annotation' => true, 152 | 'phpdoc_add_missing_param_annotation' => ['only_untyped' => false], 153 | 'phpdoc_align' => ['align' => 'vertical'], 154 | 'phpdoc_indent' => true, 155 | 'phpdoc_inline_tag_normalizer' => true, 156 | 'phpdoc_line_span' => true, 157 | 'phpdoc_no_access' => true, 158 | 'phpdoc_no_empty_return' => true, 159 | 'phpdoc_no_package' => true, 160 | 'phpdoc_no_useless_inheritdoc' => true, 161 | 'phpdoc_order_by_value' => true, 162 | 'phpdoc_order' => true, 163 | 'phpdoc_return_self_reference' => ['replacements' => ['this' => 'self']], 164 | 'phpdoc_scalar' => true, 165 | 'phpdoc_separation' => true, 166 | 'phpdoc_single_line_var_spacing' => true, 167 | 'phpdoc_summary' => true, 168 | 'phpdoc_tag_type' => true, 169 | 'phpdoc_to_comment' => ['ignored_tags' => ['var']], 170 | 'phpdoc_trim_consecutive_blank_line_separation' => true, 171 | 'phpdoc_trim' => true, 172 | 'phpdoc_types_order' => true, 173 | 'phpdoc_types' => true, 174 | 'phpdoc_var_annotation_correct_order' => true, 175 | 'phpdoc_var_without_name' => true, 176 | 'pow_to_exponentiation' => true, 177 | 'protected_to_private' => true, 178 | 'psr_autoloading' => true, 179 | 'random_api_migration' => true, 180 | 'regular_callable_call' => true, 181 | 'return_assignment' => true, 182 | 'return_type_declaration' => ['space_before' => 'none'], 183 | 'return_type_declaration' => true, 184 | 'self_accessor' => true, 185 | 'self_static_accessor' => true, 186 | 'semicolon_after_instruction' => true, 187 | 'set_type_to_cast' => true, 188 | 'short_scalar_cast' => true, 189 | 'simple_to_complex_string_variable' => true, 190 | 'simplified_if_return' => true, 191 | 'single_blank_line_at_eof' => true, 192 | 'single_blank_line_before_namespace' => true, 193 | 'single_class_element_per_statement' => true, 194 | 'single_line_after_imports' => true, 195 | 'single_line_comment_spacing' => true, 196 | 'single_line_comment_style' => ['comment_types' => ['hash']], 197 | 'single_line_throw' => true, 198 | 'single_quote' => true, 199 | 'single_space_after_construct' => true, 200 | 'single_space_around_construct' => true, 201 | 'single_trait_insert_per_statement' => true, 202 | 'space_after_semicolon' => true, 203 | 'standardize_increment' => true, 204 | 'standardize_not_equals' => true, 205 | 'statement_indentation' => true, 206 | 'static_lambda' => true, 207 | 'strict_comparison' => true, 208 | 'strict_param' => true, 209 | 'string_length_to_empty' => true, 210 | 'string_line_ending' => true, 211 | 'switch_case_semicolon_to_colon' => true, 212 | 'switch_case_space' => true, 213 | 'switch_continue_to_break' => true, 214 | 'ternary_operator_spaces' => true, 215 | 'ternary_to_elvis_operator' => true, 216 | 'ternary_to_null_coalescing' => true, 217 | 'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['arguments', 'arrays', 'match', 'parameters']], 218 | 'trim_array_spaces' => true, 219 | 'types_spaces' => ['space' => 'single', 'space_multiple_catch' => 'single'], 220 | 'unary_operator_spaces' => true, 221 | 'use_arrow_functions' => true, 222 | 'visibility_required' => true, 223 | 'void_return' => true, 224 | 'whitespace_after_comma_in_array' => true, 225 | 'yoda_style' => true, 226 | ]) 227 | ->setFinder( 228 | PhpCsFixer\Finder::create() 229 | ->exclude('vendor') 230 | ->in([__DIR__ . '/src/']), 231 | ); 232 | -------------------------------------------------------------------------------- /app/.phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- 1 | # Auth0 PHP Web App Sample 2 | 3 | This sample demonstrates how to add endpoint authorization to a [PHP](http://php.net/) backend API service using [Auth0](https://auth0.com). Check the [PHP Quickstart](https://auth0.com/docs/quickstart/backend/php) to understand this sample better. 4 | 5 | ## Configuration 6 | 7 | ### Create a free account in Auth0 8 | 9 | 1. Go to [Auth0](https://auth0.com) and click Sign Up. 10 | 2. Use Google, GitHub or Microsoft Account to login. 11 | 12 | ### Create an Auth0 Application 13 | 14 | You will need to create a Regular Web Application using the [Auth0 Dashboard](https://manage.auth0.com). This will give you a Domain, Client ID, and Client Secret you will need below. 15 | 16 | ### Configure Credentials 17 | 18 | Your project needs to be configured with your Auth0 Domain, Client ID, and Client Secret for the authentication flow to work. 19 | 20 | Copy .env.example into a new file in the same folder called .env, and replace the values with your Auth0 application credentials: 21 | 22 | ```sh 23 | # Your Auth0 application's Client ID 24 | AUTH0_CLIENT_ID='YOUR_AUTH0_CLIENT_ID' 25 | 26 | # The url of your Auth0 tenant domain 27 | AUTH0_DOMAIN='https://YOUR_AUTH0_DOMAIN.auth0.com' 28 | 29 | # Your Auth0 application's Client Secret 30 | AUTH0_CLIENT_SECRET='YOUR_AUTH0_CLIENT_SECRET' 31 | 32 | # A long secret value used to encrypt the session cookie 33 | AUTH0_COOKIE_SECRET='LONG_RANDOM_VALUE' 34 | ``` 35 | 36 | **Note**: Make sure you replace `LONG_RANDOM_VALUE` with your secret (you can generate a suitable string using `openssl rand -hex 32` on the command line). 37 | 38 | **Note**: Ensure you are consistent in your use of 'localhost' and/or '127.0.0.1' when testing. These must match your Auth0 Application settings or you will encounter errors. They must also match for session cookies to work correctly. 39 | 40 | ## Install dependencies 41 | 42 | Please ensure you have [Composer](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-macos) installed and accessible from your shell. This is required. 43 | 44 | ```bash 45 | composer install --no-dev 46 | ``` 47 | 48 | ## Run the sample 49 | 50 | Before continuing, please ensure you have [PHP](https://www.php.net/manual/en/install.php) 7.4+ and [Composer](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-macos) installed and accessible from your shell. These are required. 51 | 52 | Next, use the following command to install the necessary dependencies and start the sample: 53 | 54 | ```bash 55 | php -S 127.0.0.1:3000 public/index.php 56 | ``` 57 | 58 | Your Quickstart should now be accessible at [http://127.0.0.1:3000/](http://127.0.0.1:3000/) from your web browser. 59 | 60 | ## Vulnerability Reporting 61 | 62 | Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 63 | 64 | ## What is Auth0? 65 | 66 | Auth0 helps you to easily: 67 | 68 | - implement authentication with multiple identity providers, including social (e.g., Google, Facebook, Microsoft, LinkedIn, GitHub, Twitter, etc), or enterprise (e.g., Windows Azure AD, Google Apps, Active Directory, ADFS, SAML, etc.) 69 | - log in users with username/password databases, passwordless, or multi-factor authentication 70 | - link multiple user accounts together 71 | - generate signed JSON Web Tokens to authorize your API calls and flow the user identity securely 72 | - access demographics and analytics detailing how, when, and where users are logging in 73 | - enrich user profiles from other data sources using customizable JavaScript rules 74 | 75 | [Why Auth0?](https://auth0.com/why-auth0) 76 | 77 | ## License 78 | 79 | This project is licensed under the MIT license. See the [LICENSE](https://github.com/auth0-samples/auth0-php-web-app/blob/master/LICENSE) file for more info. 80 | -------------------------------------------------------------------------------- /app/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth0-samples/auth0-php-web-app", 3 | "description": "Auth0 Integration Samples for PHP Web Applications.", 4 | "require": { 5 | "php": "^8.0", 6 | "auth0/auth0-php": "^8.0", 7 | "guzzlehttp/guzzle": "^7.3", 8 | "guzzlehttp/psr7": "^2.2", 9 | "http-interop/http-factory-guzzle": "^1.0", 10 | "hyperf/event": "^2.1", 11 | "php-http/httplug": "^2.2", 12 | "vlucas/phpdotenv": "^5.3" 13 | }, 14 | "require-dev": { 15 | "friendsofphp/php-cs-fixer": "^3", 16 | "mockery/mockery": "^1", 17 | "nunomaduro/larastan": "^2", 18 | "orchestra/testbench": "^7 || ^8", 19 | "pestphp/pest": "^2", 20 | "pestphp/pest-plugin-laravel": "^2", 21 | "phpstan/phpstan": "^1", 22 | "phpstan/phpstan-strict-rules": "^1", 23 | "psalm/plugin-laravel": "^2", 24 | "rector/rector": "0.17.0", 25 | "spatie/laravel-ray": "^1", 26 | "squizlabs/php_codesniffer": "^3", 27 | "symfony/cache": "^6", 28 | "vimeo/psalm": "^5", 29 | "wikimedia/composer-merge-plugin": "^2" 30 | }, 31 | "config": { 32 | "optimize-autoloader": true, 33 | "sort-packages": true, 34 | "allow-plugins": { 35 | "dealerdirect/phpcodesniffer-composer-installer": true, 36 | "friendsofphp/well-known-implementations": false, 37 | "wikimedia/composer-merge-plugin": true, 38 | "php-http/discovery": false, 39 | "pestphp/pest-plugin": true 40 | } 41 | }, 42 | "scripts": { 43 | "pest:coverage": "@php vendor/bin/pest --coverage --parallel --no-progress", 44 | "pest:debug": "@php vendor/bin/pest --fail-on-risky --stop-on-defect", 45 | "pest:profile": "@php vendor/bin/pest --profile", 46 | "pest": "@php vendor/bin/pest --order-by random --fail-on-risky --stop-on-defect --coverage --parallel", 47 | "phpcs:fix": "@php vendor/bin/php-cs-fixer fix", 48 | "phpcs": "@php vendor/bin/php-cs-fixer fix --dry-run --diff", 49 | "phpstan": "@php vendor/bin/phpstan analyze", 50 | "psalm:fix": "@php vendor/bin/psalter --issues=all", 51 | "psalm": "@php vendor/bin/psalm", 52 | "rector:fix": "@php vendor/bin/rector process src", 53 | "rector": "@php vendor/bin/rector process src --dry-run", 54 | "test": [ 55 | "Composer\\Config::disableProcessTimeout", 56 | "@pest", 57 | "@phpstan", 58 | "@psalm", 59 | "@rector", 60 | "@phpcs" 61 | ] 62 | }, 63 | "license": "MIT" 64 | } 65 | -------------------------------------------------------------------------------- /app/phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - ./vendor/phpstan/phpstan-strict-rules/rules.neon 3 | - ./vendor/nunomaduro/larastan/extension.neon 4 | 5 | parameters: 6 | level: max 7 | 8 | paths: 9 | - src 10 | 11 | ignoreErrors: 12 | - '#Method (.*) has parameter (.*) with no value type specified in iterable type array.#' 13 | - '#no value type specified in iterable type array.#' 14 | 15 | reportUnmatchedIgnoredErrors: false 16 | treatPhpDocTypesAsCertain: false 17 | checkGenericClassInNonGenericObjectType: false 18 | -------------------------------------------------------------------------------- /app/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests 10 | 11 | 12 | 13 | 14 | ./src 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/psalm.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/public/bootstrap.php: -------------------------------------------------------------------------------- 1 | load(); 30 | 31 | // Instantiate our Quickstart Application using the .env configuration. 32 | $app = new Application($_ENV); 33 | 34 | $app->run(); 35 | -------------------------------------------------------------------------------- /app/rector.php: -------------------------------------------------------------------------------- 1 | paths([ 209 | __DIR__ . '/config', 210 | __DIR__ . '/src', 211 | ]); 212 | 213 | $rectorConfig->ruleWithConfiguration( 214 | RenameFunctionRector::class, 215 | [ 216 | 'chop' => 'rtrim', 217 | 'doubleval' => 'floatval', 218 | 'fputs' => 'fwrite', 219 | 'gzputs' => 'gzwrites', 220 | 'ini_alter' => 'ini_set', 221 | 'is_double' => 'is_float', 222 | 'is_integer' => 'is_int', 223 | 'is_long' => 'is_int', 224 | 'is_real' => 'is_float', 225 | 'is_writeable' => 'is_writable', 226 | 'join' => 'implode', 227 | 'key_exists' => 'array_key_exists', 228 | 'mbstrcut' => 'mb_strcut', 229 | 'mbstrlen' => 'mb_strlen', 230 | 'mbstrpos' => 'mb_strpos', 231 | 'mbstrrpos' => 'mb_strrpos', 232 | 'mbsubstr' => 'mb_substr', 233 | 'pos' => 'current', 234 | 'sizeof' => 'count', 235 | 'split' => 'explode', 236 | 'strchr' => 'strstr', 237 | ], 238 | ); 239 | 240 | $rectorConfig->ruleWithConfiguration( 241 | StaticCallToFuncCallRector::class, 242 | [ 243 | new StaticCallToFuncCall('Nette\\Utils\\Strings', 'contains', 'str_contains'), 244 | new StaticCallToFuncCall('Nette\\Utils\\Strings', 'endsWith', 'str_ends_with'), 245 | new StaticCallToFuncCall('Nette\\Utils\\Strings', 'startsWith', 'str_starts_with'), 246 | ], 247 | ); 248 | 249 | $rectorConfig->ruleWithConfiguration( 250 | ArgumentAdderRector::class, 251 | [new ArgumentAdder('Nette\\Utils\\Strings', 'replace', 2, 'replacement', '')], 252 | ); 253 | 254 | $rectorConfig->ruleWithConfiguration( 255 | RenameFunctionRector::class, 256 | [ 257 | 'pg_clientencoding' => 'pg_client_encoding', 258 | 'pg_cmdtuples' => 'pg_affected_rows', 259 | 'pg_errormessage' => 'pg_last_error', 260 | 'pg_fieldisnull' => 'pg_field_is_null', 261 | 'pg_fieldname' => 'pg_field_name', 262 | 'pg_fieldnum' => 'pg_field_num', 263 | 'pg_fieldprtlen' => 'pg_field_prtlen', 264 | 'pg_fieldsize' => 'pg_field_size', 265 | 'pg_fieldtype' => 'pg_field_type', 266 | 'pg_freeresult' => 'pg_free_result', 267 | 'pg_getlastoid' => 'pg_last_oid', 268 | 'pg_loclose' => 'pg_lo_close', 269 | 'pg_locreate' => 'pg_lo_create', 270 | 'pg_loexport' => 'pg_lo_export', 271 | 'pg_loimport' => 'pg_lo_import', 272 | 'pg_loopen' => 'pg_lo_open', 273 | 'pg_loread' => 'pg_lo_read', 274 | 'pg_loreadall' => 'pg_lo_read_all', 275 | 'pg_lounlink' => 'pg_lo_unlink', 276 | 'pg_lowrite' => 'pg_lo_write', 277 | 'pg_numfields' => 'pg_num_fields', 278 | 'pg_numrows' => 'pg_num_rows', 279 | 'pg_result' => 'pg_fetch_result', 280 | 'pg_setclientencoding' => 'pg_set_client_encoding', 281 | ], 282 | ); 283 | 284 | $rectorConfig->ruleWithConfiguration( 285 | FunctionArgumentDefaultValueReplacerRector::class, 286 | [ 287 | new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge'), 288 | new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'), 289 | new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '', '!='), 290 | new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '!', '!='), 291 | new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'g', 'gt'), 292 | new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'l', 'lt'), 293 | new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge'), 294 | new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'), 295 | new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'n', 'ne'), 296 | ], 297 | ); 298 | 299 | $rectorConfig->ruleWithConfiguration( 300 | FuncCallToConstFetchRector::class, 301 | [ 302 | 'php_sapi_name' => 'PHP_SAPI', 303 | 'pi' => 'M_PI', 304 | ], 305 | ); 306 | 307 | $rectorConfig->rules([ 308 | AbsolutizeRequireAndIncludePathRector::class, 309 | ActionInjectionToConstructorInjectionRector::class, 310 | AddArrayDefaultToArrayPropertyRector::class, 311 | AddArrowFunctionReturnTypeRector::class, 312 | AddClosureReturnTypeRector::class, 313 | AddMethodCallBasedStrictParamTypeRector::class, 314 | AddParamBasedOnParentClassMethodRector::class, 315 | AddParamTypeBasedOnPHPUnitDataProviderRector::class, 316 | AddParamTypeSplFixedArrayRector::class, 317 | AddReturnTypeDeclarationBasedOnParentClassMethodRector::class, 318 | AddReturnTypeDeclarationFromYieldsRector::class, 319 | AddVoidReturnTypeWhereNoReturnRector::class, 320 | AndAssignsToSeparateLinesRector::class, 321 | ArrayKeyExistsTernaryThenValueToCoalescingRector::class, 322 | ArrayKeysAndInArrayToArrayKeyExistsRector::class, 323 | ArrayMergeOfNonArraysToSimpleArrayRector::class, 324 | ArrayShapeFromConstantArrayReturnRector::class, 325 | BinarySwitchToIfElseRector::class, 326 | BooleanNotIdenticalToNotIdenticalRector::class, 327 | BoolvalToTypeCastRector::class, 328 | CallableThisArrayToAnonymousFunctionRector::class, 329 | CallUserFuncArrayToVariadicRector::class, 330 | CallUserFuncToMethodCallRector::class, 331 | CallUserFuncToMethodCallRector::class, 332 | CallUserFuncWithArrowFunctionToInlineRector::class, 333 | CatchExceptionNameMatchingTypeRector::class, 334 | ChangeArrayPushToArrayAssignRector::class, 335 | ChangeGlobalVariablesToPropertiesRector::class, 336 | ChangeIfElseValueAssignToEarlyReturnRector::class, 337 | ChangeNestedForeachIfsToEarlyContinueRector::class, 338 | ChangeNestedIfsToEarlyReturnRector::class, 339 | ChangeOrIfContinueToMultiContinueRector::class, 340 | ChangeSwitchToMatchRector::class, 341 | ClassOnObjectRector::class, 342 | ClassOnThisVariableObjectRector::class, 343 | ClassPropertyAssignToConstructorPromotionRector::class, 344 | CombinedAssignRector::class, 345 | CombineIfRector::class, 346 | CommonNotEqualRector::class, 347 | CompactToVariablesRector::class, 348 | CompleteDynamicPropertiesRector::class, 349 | ConsecutiveNullCompareReturnsToNullCoalesceQueueRector::class, 350 | ConsistentImplodeRector::class, 351 | CountArrayToEmptyArrayComparisonRector::class, 352 | CountArrayToEmptyArrayComparisonRector::class, 353 | EmptyOnNullableObjectToInstanceOfRector::class, 354 | EncapsedStringsToSprintfRector::class, 355 | ExplicitBoolCompareRector::class, 356 | FinalizeClassesWithoutChildrenRector::class, 357 | FinalPrivateToPrivateVisibilityRector::class, 358 | FlipTypeControlToUseExclusiveTypeRector::class, 359 | FloatvalToTypeCastRector::class, 360 | ForeachItemsAssignToEmptyArrayToAssignRector::class, 361 | ForeachToInArrayRector::class, 362 | ForRepeatedCountToOwnVariableRector::class, 363 | FuncGetArgsToVariadicParamRector::class, 364 | FuncGetArgsToVariadicParamRector::class, 365 | GetClassToInstanceOfRector::class, 366 | GetDebugTypeRector::class, 367 | InlineArrayReturnAssignRector::class, 368 | InlineConstructorDefaultToPropertyRector::class, 369 | InlineIfToExplicitIfRector::class, 370 | InlineIsAInstanceOfRector::class, 371 | IntvalToTypeCastRector::class, 372 | IsAWithStringWithThirdArgumentRector::class, 373 | IssetOnPropertyObjectToPropertyExistsRector::class, 374 | JoinStringConcatRector::class, 375 | LogicalToBooleanRector::class, 376 | MakeInheritedMethodVisibilitySameAsParentRector::class, 377 | MultipleClassFileToPsr4ClassesRector::class, 378 | NewlineBeforeNewAssignSetRector::class, 379 | NewStaticToNewSelfRector::class, 380 | NormalizeNamespaceByPSR4ComposerAutoloadRector::class, 381 | NullableCompareToNullRector::class, 382 | OptionalParametersAfterRequiredRector::class, 383 | OptionalParametersAfterRequiredRector::class, 384 | ParamTypeByMethodCallTypeRector::class, 385 | ParamTypeByParentCallTypeRector::class, 386 | ParamTypeFromStrictTypedPropertyRector::class, 387 | Php8ResourceReturnToObjectRector::class, 388 | PostIncDecToPreIncDecRector::class, 389 | PreparedValueToEarlyReturnRector::class, 390 | PrivatizeFinalClassMethodRector::class, 391 | PrivatizeFinalClassPropertyRector::class, 392 | PropertyTypeFromStrictSetterGetterRector::class, 393 | RemoveAlwaysElseRector::class, 394 | RemoveAlwaysTrueConditionSetInConstructorRector::class, 395 | RemoveAndTrueRector::class, 396 | RemoveDeadConditionAboveReturnRector::class, 397 | RemoveDeadContinueRector::class, 398 | RemoveDeadIfForeachForRector::class, 399 | RemoveDeadLoopRector::class, 400 | RemoveDeadReturnRector::class, 401 | RemoveDeadStmtRector::class, 402 | RemoveDeadTryCatchRector::class, 403 | RemoveDeadZeroAndOneOperationRector::class, 404 | RemoveDelegatingParentCallRector::class, 405 | RemoveDoubleAssignRector::class, 406 | RemoveDuplicatedArrayKeyRector::class, 407 | RemoveDuplicatedCaseInSwitchRector::class, 408 | RemoveEmptyClassMethodRector::class, 409 | RemoveEmptyTestMethodRector::class, 410 | RemoveExtraParametersRector::class, 411 | RemoveFinalFromConstRector::class, 412 | RemoveJustPropertyFetchForAssignRector::class, 413 | RemoveJustVariableAssignRector::class, 414 | RemoveLastReturnRector::class, 415 | RemoveNonExistingVarAnnotationRector::class, 416 | RemoveNullPropertyInitializationRector::class, 417 | RemoveParentCallWithoutParentRector::class, 418 | RemoveParentCallWithoutParentRector::class, 419 | RemoveSoleValueSprintfRector::class, 420 | RemoveUnreachableStatementRector::class, 421 | RemoveUnusedConstructorParamRector::class, 422 | RemoveUnusedForeachKeyRector::class, 423 | RemoveUnusedNonEmptyArrayBeforeForeachRector::class, 424 | RemoveUnusedPrivateClassConstantRector::class, 425 | RemoveUnusedPrivateMethodParameterRector::class, 426 | RemoveUnusedPrivatePropertyRector::class, 427 | RemoveUnusedPromotedPropertyRector::class, 428 | RemoveUnusedVariableAssignRector::class, 429 | RemoveUnusedVariableInCatchRector::class, 430 | RemoveUselessReturnTagRector::class, 431 | RemoveUselessVarTagRector::class, 432 | RenameForeachValueVariableToMatchExprVariableRector::class, 433 | RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class, 434 | ReplaceMultipleBooleanNotRector::class, 435 | ReturnAnnotationIncorrectNullableRector::class, 436 | ReturnBinaryAndToEarlyReturnRector::class, 437 | ReturnBinaryOrToEarlyReturnRector::class, 438 | ReturnEarlyIfVariableRector::class, 439 | ReturnNeverTypeRector::class, 440 | ReturnTypeFromReturnDirectArrayRector::class, 441 | ReturnTypeFromReturnNewRector::class, 442 | ReturnTypeFromStrictBoolReturnExprRector::class, 443 | ReturnTypeFromStrictConstantReturnRector::class, 444 | ReturnTypeFromStrictNativeCallRector::class, 445 | ReturnTypeFromStrictNewArrayRector::class, 446 | ReturnTypeFromStrictScalarReturnExprRector::class, 447 | ReturnTypeFromStrictScalarReturnExprRector::class, 448 | ReturnTypeFromStrictTernaryRector::class, 449 | ReturnTypeFromStrictTypedCallRector::class, 450 | ReturnTypeFromStrictTypedPropertyRector::class, 451 | SeparateMultiUseImportsRector::class, 452 | SetStateToStaticRector::class, 453 | SetTypeToCastRector::class, 454 | ShortenElseIfRector::class, 455 | SimplifyArraySearchRector::class, 456 | SimplifyBoolIdenticalTrueRector::class, 457 | SimplifyConditionsRector::class, 458 | SimplifyDeMorganBinaryRector::class, 459 | SimplifyEmptyArrayCheckRector::class, 460 | SimplifyEmptyCheckOnEmptyArrayRector::class, 461 | SimplifyForeachToArrayFilterRector::class, 462 | SimplifyForeachToCoalescingRector::class, 463 | SimplifyFuncGetArgsCountRector::class, 464 | SimplifyIfElseToTernaryRector::class, 465 | SimplifyIfElseWithSameContentRector::class, 466 | SimplifyIfNotNullReturnRector::class, 467 | SimplifyIfNullableReturnRector::class, 468 | SimplifyIfReturnBoolRector::class, 469 | SimplifyInArrayValuesRector::class, 470 | SimplifyMirrorAssignRector::class, 471 | SimplifyRegexPatternRector::class, 472 | SimplifyStrposLowerRector::class, 473 | SimplifyTautologyTernaryRector::class, 474 | SimplifyUselessVariableRector::class, 475 | SingleInArrayToCompareRector::class, 476 | SingularSwitchToIfRector::class, 477 | SplitDoubleAssignRector::class, 478 | SplitGroupedClassConstantsRector::class, 479 | SplitGroupedPropertiesRector::class, 480 | StaticArrowFunctionRector::class, 481 | StaticClosureRector::class, 482 | StrContainsRector::class, 483 | StrEndsWithRector::class, 484 | StrictArraySearchRector::class, 485 | StringableForToStringRector::class, 486 | StrlenZeroToIdenticalEmptyStringRector::class, 487 | StrStartsWithRector::class, 488 | StrvalToTypeCastRector::class, 489 | SwitchNegatedTernaryRector::class, 490 | SymplifyQuoteEscapeRector::class, 491 | TernaryConditionVariableAssignmentRector::class, 492 | TernaryEmptyArrayArrayDimFetchToCoalesceRector::class, 493 | TernaryFalseExpressionToIfRector::class, 494 | TernaryToBooleanOrFalseToBooleanAndRector::class, 495 | ThrowWithPreviousExceptionRector::class, 496 | TypedPropertyFromAssignsRector::class, 497 | TypedPropertyFromStrictConstructorRector::class, 498 | TypedPropertyFromStrictGetterMethodReturnTypeRector::class, 499 | TypedPropertyFromStrictSetUpRector::class, 500 | UnnecessaryTernaryExpressionRector::class, 501 | UnSpreadOperatorRector::class, 502 | UnusedForeachValueToArrayKeysRector::class, 503 | UnwrapFutureCompatibleIfPhpVersionRector::class, 504 | UnwrapSprintfOneArgumentRector::class, 505 | UseClassKeywordForClassNameResolutionRector::class, 506 | UseIdenticalOverEqualWithSameTypeRector::class, 507 | UseIncrementAssignRector::class, 508 | VarAnnotationIncorrectNullableRector::class, 509 | VarConstantCommentRector::class, 510 | VarToPublicPropertyRector::class, 511 | VersionCompareFuncCallToConstantRector::class, 512 | WrapEncapsedVariableInCurlyBracesRector::class, 513 | IfIssetToCoalescingRector::class, 514 | CleanupUnneededNullsafeOperatorRector::class, 515 | BoolReturnTypeFromStrictScalarReturnsRector::class, 516 | ]); 517 | }; 518 | -------------------------------------------------------------------------------- /app/src/Application.php: -------------------------------------------------------------------------------- 1 | $env Auth0 configuration imported from .env file. 42 | */ 43 | public function __construct( 44 | array $env, 45 | ) { 46 | // Configure the SDK using our .env configuration. 47 | $this->setupAuth0($env); 48 | 49 | // Setup our template engine, for sending responses back to the browser. 50 | $this->templates = new ApplicationTemplates($this); 51 | $this->errorHandler = new ApplicationErrorHandler($this); 52 | $this->router = new ApplicationRouter($this); 53 | } 54 | 55 | /** 56 | * Return our instance of Auth0. 57 | */ 58 | public function &getSdk(): Auth0 59 | { 60 | return $this->sdk; 61 | } 62 | 63 | /** 64 | * Return our instance of SdkConfiguration. 65 | */ 66 | public function &getConfiguration(): SdkConfiguration 67 | { 68 | return $this->configuration; 69 | } 70 | 71 | /** 72 | * Return our instance of ApplicationTemplates. 73 | */ 74 | public function &getTemplate(): ApplicationTemplates 75 | { 76 | return $this->templates; 77 | } 78 | 79 | /** 80 | * Return our instance of ApplicationErrorHandler. 81 | */ 82 | public function &getErrorHandler(): ApplicationErrorHandler 83 | { 84 | return $this->errorHandler; 85 | } 86 | 87 | /** 88 | * Return our instance of ApplicationRouter. 89 | */ 90 | public function &getRouter(): ApplicationRouter 91 | { 92 | return $this->router; 93 | } 94 | 95 | /** 96 | * Called from the ApplicationRouter when end user loads '/api'. 97 | * 98 | * @param ApplicationRouter $router 99 | */ 100 | public function onApiRoute( 101 | ApplicationRouter $router, 102 | ): void { 103 | $session = $this->getToken(); 104 | 105 | // Send response to browser. 106 | $this->templates->render('logged-' . ($session instanceof \Auth0\SDK\Contract\TokenInterface ? 'in' : 'out'), [ 107 | 'session' => $session, 108 | 'router' => $router, 109 | ]); 110 | } 111 | 112 | /** 113 | * Called from the ApplicationRouter when end user loads an unknown route. 114 | * 115 | * @param ApplicationRouter $router 116 | */ 117 | public function onError404( 118 | ApplicationRouter $router, 119 | ): void { 120 | $router->setHttpStatus(404); 121 | } 122 | 123 | /** 124 | * Called from the ApplicationRouter when end user loads '/'. 125 | * 126 | * @param ApplicationRouter $router 127 | */ 128 | public function onIndexRoute( 129 | ApplicationRouter $router, 130 | ): void { 131 | // Send response to browser. 132 | $this->templates->render('spa', [ 133 | 'config' => $this->getConfiguration(), 134 | 'router' => $router, 135 | ]); 136 | } 137 | 138 | /** 139 | * "Run" our application, responding to end-user requests. 140 | */ 141 | public function run(): void 142 | { 143 | // Intercept exceptions to gracefully report them. 144 | $this->errorHandler->hook(); 145 | 146 | // Handle incoming requests through the router. 147 | $this->router->run(); 148 | } 149 | 150 | /** 151 | * Configure the Auth0 SDK using the .env configuration. 152 | * 153 | * @param array $env Auth0 configuration imported from .env file. 154 | */ 155 | public function setupAuth0( 156 | array $env, 157 | ): void { 158 | // Build our SdkConfiguration. 159 | $this->configuration = new SdkConfiguration([ 160 | 'domain' => $env['AUTH0_DOMAIN'] ?? null, 161 | 'clientId' => $env['AUTH0_CLIENT_ID'] ?? null, 162 | 'clientSecret' => $env['AUTH0_CLIENT_SECRET'] ?? null, 163 | 'audience' => ($env['AUTH0_AUDIENCE'] ?? null) !== null ? [trim($env['AUTH0_AUDIENCE'])] : null, 164 | 'organization' => ($env['AUTH0_ORGANIZATION'] ?? null) !== null ? [trim($env['AUTH0_ORGANIZATION'])] : null, 165 | 'strategy' => SdkConfiguration::STRATEGY_API, 166 | ]); 167 | 168 | // Setup the Auth0 SDK. 169 | $this->sdk = new Auth0($this->configuration); 170 | } 171 | 172 | /** 173 | * Process a token from the request query or via request headers. 174 | * 175 | * @throws InvalidTokenException When an invalid token is provided. 176 | */ 177 | private function getToken(): ?\Auth0\SDK\Contract\TokenInterface 178 | { 179 | // Look for token in ?token=... param, followed by an `HTTP_AUTHORIZATION` or `Authorization` header. 180 | $token = $_GET['token'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['Authorization'] ?? null; 181 | 182 | // If no token was present, abort processing. 183 | if (null === $token) { 184 | return null; 185 | } 186 | 187 | // Trim whitespace from token string. 188 | $token = trim($token); 189 | 190 | // Remove the 'Bearer ' prefix, if present, in the event we're using an Authorization header that's using it. 191 | if (str_starts_with($token, 'Bearer ')) { 192 | $token = substr($token, 7); 193 | } 194 | 195 | return $this->getSdk()->decode($token, null, null, null, null, null, null, \Auth0\SDK\Token::TYPE_TOKEN); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /app/src/ApplicationErrorHandler.php: -------------------------------------------------------------------------------- 1 | app = &$app; 28 | } 29 | 30 | /** 31 | * Setup onException() as the exception handler with PHP for this request. 32 | */ 33 | public function hook(): void 34 | { 35 | set_exception_handler(function (Throwable $throwable): void { 36 | $this->onException($throwable); 37 | }); 38 | } 39 | 40 | /** 41 | * Render a throwable error in a graceful way. 42 | * 43 | * @param Throwable $throwable The throwable to report. 44 | */ 45 | public function onException( 46 | Throwable $throwable, 47 | ): void { 48 | $exception = $throwable; 49 | 50 | $code = $exception->getCode(); 51 | $error = $exception->getMessage(); 52 | $file = $exception->getFile(); 53 | $line = $exception->getLine(); 54 | 55 | if ($exception instanceof Auth0Exception) { 56 | $backtrace = $exception->getTrace(); 57 | 58 | if (array_key_exists(1, $backtrace)) { 59 | $code = $backtrace[1]['line']; 60 | $file = $backtrace[1]['file']; 61 | $line = $backtrace[1]['line']; 62 | } 63 | } 64 | 65 | $this->app->getTemplate()->render('error', [ 66 | 'code' => $code, 67 | 'error' => $error, 68 | 'file' => $file, 69 | 'line' => $line, 70 | 'router' => $this->app->getRouter(), 71 | ]); 72 | 73 | exit; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/ApplicationRouter.php: -------------------------------------------------------------------------------- 1 | app = &$app; 25 | } 26 | 27 | /** 28 | * Return the request method (GET, POST, etc.). 29 | */ 30 | public function getMethod(): string 31 | { 32 | return $_SERVER['REQUEST_METHOD'] ?? 'GET'; 33 | } 34 | 35 | /** 36 | * Return (and optionally manipulate) the currently requested uri. 37 | * 38 | * @param null|string $path Unless null, manipulates the resulting path to match the value. 39 | * @param null|string $query Unless, manipulates the resulting query to match the value. 40 | */ 41 | public function getUri( 42 | ?string $path = null, 43 | ?string $query = null, 44 | ): string { 45 | $httpScheme = $_SERVER['HTTPS'] ?? ''; 46 | $httpScheme = 'on' === $httpScheme ? 'https' : 'http'; 47 | 48 | $httpPort = (int) $_SERVER['SERVER_PORT']; 49 | $httpHost = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME']; 50 | $httpHost = preg_replace('/\:' . $httpPort . '$/', '', $httpHost); 51 | 52 | $httpRequest = (string) $_SERVER['REQUEST_URI']; 53 | $httpUri = $httpScheme . '://' . $httpHost . (80 !== $httpPort ? ':' . $httpPort : '') . $httpRequest; 54 | 55 | // If we aren't making changes, simply return the uri. 56 | if (null === $path && null === $query) { 57 | return $httpUri; 58 | } 59 | 60 | // Parse a url into it's components so we can manipulate them more easily. 61 | $parsedUri = parse_url($httpUri); 62 | 63 | if (false === $parsedUri) { 64 | return $httpUri; 65 | } 66 | 67 | $parsedUri['scheme'] ??= 'http'; 68 | $parsedUri['host'] ??= $httpHost; 69 | $parsedUri['path'] ??= ''; 70 | $parsedUri['query'] = '?' . ($parsedUri['query'] ?? ''); 71 | 72 | // Manipulate the /path portion of the uri. 73 | if (null !== $path) { 74 | $parsedUri['path'] = $path; 75 | } 76 | 77 | // Manipulate the ?query portion of the uri. 78 | if (null !== $query) { 79 | $parsedUri['query'] = $query; 80 | } 81 | 82 | if (! array_key_exists('port', $parsedUri)) { 83 | $parsedUri['port'] = 80; 84 | } 85 | 86 | if ('?' === $parsedUri['query']) { 87 | $parsedUri['query'] = ''; 88 | } 89 | 90 | if ('' !== $parsedUri['query']) { 91 | $parsedUri['query'] = '?' . $parsedUri['query']; 92 | } 93 | 94 | // Reconstruct the manipulated uri and return it. 95 | return $parsedUri['scheme'] . '://' . $parsedUri['host'] . (80 !== $parsedUri['port'] ? ':' . $parsedUri['port'] : '') . $parsedUri['path'] . $parsedUri['query']; 96 | } 97 | 98 | /** 99 | * Process the current request and route it to the class handler. 100 | * 101 | * @param string $uri The new uri to redirect the end user to. 102 | */ 103 | public function redirect( 104 | string $uri, 105 | ): void { 106 | header('Location: ' . $uri, true, 303); 107 | exit; 108 | } 109 | 110 | /** 111 | * Process the current request and route it to the class handler. 112 | */ 113 | public function run(): void 114 | { 115 | $requestUri = parse_url($this->getUri(), PHP_URL_PATH); 116 | 117 | $routed = false; 118 | 119 | if ('/' === $requestUri) { 120 | $this->app->onIndexRoute($this); 121 | $routed = true; 122 | } 123 | 124 | if ('/api' === $requestUri) { 125 | $this->setHeaders(); 126 | $this->app->onApiRoute($this); 127 | $routed = true; 128 | } 129 | 130 | if (false === $routed) { 131 | $this->setHeaders(); 132 | $this->app->onError404($this); 133 | } 134 | 135 | exit; 136 | } 137 | 138 | /** 139 | * Issue HTTP headers to disable browser caching. 140 | */ 141 | public function setHeaders(): void 142 | { 143 | header('Access-Control-Allow-Origin: *'); 144 | header('Content-Type: application/json; charset=UTF-8'); 145 | header('Access-Control-Allow-Methods: GET'); 146 | header('Access-Control-Max-Age: 3600'); 147 | header('Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With'); 148 | header('Cache-Control: no-cache, must-revalidate'); 149 | header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); 150 | } 151 | 152 | /** 153 | * Issue a HTTP response code header. 154 | * 155 | * @param int $status The HTTP status code to send. 156 | */ 157 | public function setHttpStatus( 158 | int $status, 159 | ): void { 160 | http_response_code($status); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /app/src/ApplicationTemplates.php: -------------------------------------------------------------------------------- 1 | , layout: null|array{name: string, variables: array}} 24 | */ 25 | private array $state = [ 26 | 'sections' => [], 27 | 'section' => null, 28 | 'layout' => null, 29 | ]; 30 | 31 | /** 32 | * ApplicationTemplates constructor. 33 | * 34 | * @param Application $app An instance of our Quickstart Application. 35 | */ 36 | public function __construct( 37 | Application &$app, 38 | ) { 39 | $this->app = &$app; 40 | } 41 | 42 | /** 43 | * Render a template as the browser response, then exit. 44 | * 45 | * @param string $template The name of the template to use. 46 | * @param array $variables Any variables the template should have access to use. 47 | */ 48 | public function render( 49 | string $template, 50 | array $variables = [], 51 | ): void { 52 | $this->state = [ 53 | 'sections' => [], 54 | 'section' => null, 55 | 'layout' => null, 56 | ]; 57 | 58 | while (ob_get_level() >= 1) { 59 | ob_end_clean(); 60 | } 61 | 62 | echo $this->renderTemplate($template, $variables); 63 | exit; 64 | } 65 | 66 | /** 67 | * Define a container layout in which to render a template. 68 | * 69 | * @param string $name The name of the layout template to use. 70 | * @param array $variables Any additional variables the layout template should have access to use. 71 | */ 72 | private function layout( 73 | string $name, 74 | array $variables = [], 75 | ): void { 76 | $this->state['layout'] = [ 77 | 'name' => $name, 78 | 'variables' => $variables, 79 | ]; 80 | } 81 | 82 | /** 83 | * Render a template, and return the content as a string. 84 | * 85 | * @param string $template The name of the template to use. 86 | * @param array $variables Any variables the template should have access to use. 87 | */ 88 | private function renderTemplate( 89 | string $template, 90 | array $variables = [], 91 | ): string { 92 | // Keep track of the output buffering 'level'. 93 | $level = 0; 94 | 95 | // Resolve the requested template to it's file path: 96 | $templatePath = implode(DIRECTORY_SEPARATOR, [APP_ROOT, 'templates', $template . '.php']); 97 | 98 | // Extract $variables into current scope, for use in template. 99 | extract($variables); 100 | 101 | if (! file_exists($templatePath)) { 102 | throw new Exception(sprintf('Template file not found: %s', $template)); 103 | } 104 | 105 | try { 106 | $level = ob_get_level(); 107 | ob_start(); 108 | 109 | include $templatePath; 110 | 111 | $content = ob_get_clean(); 112 | 113 | if (null !== $this->state['layout']) { 114 | $layoutTemplate = $this->state['layout']['name']; 115 | $layoutVariables = array_merge($variables, $this->state['layout']['variables']); 116 | 117 | $this->state['sections']['content'] = $content; 118 | $this->state['layout'] = null; 119 | 120 | $content = $this->renderTemplate($layoutTemplate, $layoutVariables); 121 | } 122 | 123 | if (false !== $content) { 124 | return trim($content); 125 | } 126 | 127 | return ''; 128 | } catch (Throwable $throwable) { 129 | while (ob_get_level() > $level) { 130 | ob_end_clean(); 131 | } 132 | 133 | throw $throwable; 134 | } 135 | } 136 | 137 | /** 138 | * Render a section into the template, if it's content has been set. 139 | * 140 | * @param string $sectionName Name of the section to render into the template. 141 | */ 142 | private function section( 143 | string $sectionName, 144 | ): string { 145 | return $this->state['sections'][$sectionName] ?? ''; 146 | } 147 | 148 | /** 149 | * Start capturing a template block as new section content. 150 | * 151 | * @param string $sectionName Name of the section to begin capturing. 152 | */ 153 | private function start( 154 | string $sectionName, 155 | ): void { 156 | if (null !== $this->state['section']) { 157 | throw new LogicException('Nested sections are not supported.'); 158 | } 159 | 160 | $this->state['section'] = $sectionName; 161 | 162 | ob_start(); 163 | } 164 | 165 | /** 166 | * Stop capturing a previously started template block, and store the section content for use. 167 | */ 168 | private function stop(): void 169 | { 170 | if (null === $this->state['section']) { 171 | throw new LogicException('You must start a section before stopping it.'); 172 | } 173 | 174 | if (array_key_exists($this->state['section'], $this->state['sections'])) { 175 | $this->state['sections'][$this->state['section']] = ''; 176 | } 177 | 178 | $this->state['sections'][$this->state['section']] = ob_get_clean(); 179 | $this->state['section'] = null; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /app/src/Contract/QuickstartExample.php: -------------------------------------------------------------------------------- 1 | section('body')) ?>, 3 | "meta": { 4 | "bugs": "https://github.com/auth0-samples/auth0-php-api-samples", 5 | "questions": "https://community.auth0.com/tag/auth0-php" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/templates/error.php: -------------------------------------------------------------------------------- 1 | layout('_json'); 3 | $this->start('body'); 4 | ?> 5 | "errors": [ 6 | { 7 | "title": "" 8 | } 9 | ] 10 | stop() ?> 11 | -------------------------------------------------------------------------------- /app/templates/logged-in.php: -------------------------------------------------------------------------------- 1 | layout('_json'); 3 | $this->start('body'); 4 | ?> 5 | "data": { 6 | "authenticated": true, 7 | "user": toArray(), JSON_PRETTY_PRINT); 9 | 10 | if ($user !== false) { 11 | echo str_replace('}', "\t}", str_replace(' ', "\t\t", $user)); 12 | } else { 13 | echo 'Error processing token'; 14 | } 15 | ?> 16 | 17 | } 18 | stop() ?> 19 | -------------------------------------------------------------------------------- /app/templates/logged-out.php: -------------------------------------------------------------------------------- 1 | layout('_json'); 3 | $this->start('body'); 4 | ?> 5 | "data": { 6 | "authenticated": false 7 | } 8 | stop() ?> 9 | -------------------------------------------------------------------------------- /app/templates/spa.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Auth0 PHP Quickstart for Backend APIs 11 | 12 | 25 | 26 | 27 | 28 |
29 | 39 |
40 |
41 | 61 |
62 | 63 |
64 |
65 |
66 | 67 | Loading... 68 | PHP Quickstart 69 | 72 | 73 | 74 |

75 | Welcome 76 | Guest 77 |

78 | 79 |

80 |
81 |
82 |
83 |
84 |
85 | 86 |
87 |
88 |
89 | 101 | 102 |
103 |
104 |
105 |
106 |
107 |

Example API Response

108 |
109 | 110 |
111 |
112 | 113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | 123 | 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /app/tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /app/tests/Pest.php: -------------------------------------------------------------------------------- 1 | in('Feature'); 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | Expectations 19 | |-------------------------------------------------------------------------- 20 | | 21 | | When you're writing tests, you often need to check that values meet certain conditions. The 22 | | "expect()" function gives you access to a set of "expectations" methods that you can use 23 | | to assert different things. Of course, you may extend the Expectation API at any time. 24 | | 25 | */ 26 | 27 | expect()->extend('toBeOne', function () { 28 | return $this->toBe(1); 29 | }); 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Functions 34 | |-------------------------------------------------------------------------- 35 | | 36 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 37 | | project that you don't want to repeat in every file. Here you can also expose helpers as 38 | | global functions to help you to reduce the number of lines of code in your test files. 39 | | 40 | */ 41 | 42 | function something() 43 | { 44 | // .. 45 | } 46 | -------------------------------------------------------------------------------- /app/tests/TestCase.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | --------------------------------------------------------------------------------