├── .credo.exs ├── .dialyzer.ignore-warnings ├── .formatter.exs ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ └── bug_report.md ├── dependabot.yml └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── .tool-versions ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CREDITS.md ├── LICENSE.md ├── README.md ├── RELEASE.md ├── config └── config.exs ├── guides └── config.md ├── lib └── simple_token_authentication.ex ├── mix.exs ├── mix.lock └── test ├── simple_token_authentication_test.exs └── test_helper.exs /.credo.exs: -------------------------------------------------------------------------------- 1 | # Last updated for credo 1.6.1 2 | %{ 3 | # 4 | # You can have as many configs as you like in the `configs:` field. 5 | configs: [ 6 | %{ 7 | # 8 | # Run any config using `mix credo -C `. If no config name is given 9 | # "default" is used. 10 | # 11 | name: "default", 12 | # 13 | # These are the files included in the analysis: 14 | files: %{ 15 | # 16 | # You can give explicit globs or simply directories. 17 | # In the latter case `**/*.{ex,exs}` will be used. 18 | # 19 | included: [ 20 | "lib/", 21 | "src/", 22 | "test/", 23 | "web/", 24 | "apps/*/lib/", 25 | "apps/*/src/", 26 | "apps/*/test/", 27 | "apps/*/web/" 28 | ], 29 | excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"] 30 | }, 31 | # 32 | # Load and configure plugins here: 33 | # 34 | plugins: [], 35 | # 36 | # If you create your own checks, you must specify the source files for 37 | # them here, so they can be loaded by Credo before running the analysis. 38 | # 39 | requires: [], 40 | # 41 | # If you want to enforce a style guide and need a more traditional linting 42 | # experience, you can change `strict` to `true` below: 43 | # 44 | strict: true, 45 | # 46 | # To modify the timeout for parsing files, change this value: 47 | # 48 | parse_timeout: 5000, 49 | # 50 | # If you want to use uncolored output by default, you can change `color` 51 | # to `false` below: 52 | # 53 | color: true, 54 | # 55 | # You can customize the parameters of any check by adding a second element 56 | # to the tuple. 57 | # 58 | # To disable a check put `false` as second element: 59 | # 60 | # {Credo.Check.Design.DuplicatedCode, false} 61 | # 62 | checks: %{ 63 | enabled: [ 64 | # 65 | ## Consistency Checks 66 | # 67 | {Credo.Check.Consistency.ExceptionNames, []}, 68 | {Credo.Check.Consistency.LineEndings, []}, 69 | {Credo.Check.Consistency.ParameterPatternMatching, []}, 70 | {Credo.Check.Consistency.SpaceAroundOperators, []}, 71 | {Credo.Check.Consistency.SpaceInParentheses, []}, 72 | {Credo.Check.Consistency.TabsOrSpaces, []}, 73 | 74 | # 75 | ## Design Checks 76 | # 77 | # You can customize the priority of any check 78 | # Priority values are: `low, normal, high, higher` 79 | # 80 | {Credo.Check.Design.AliasUsage, 81 | priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0}, 82 | {Credo.Check.Design.SkipTestWithoutComment, []}, 83 | # You can also customize the exit_status of each check. 84 | # If you don't want TODO comments to cause `mix credo` to fail, just 85 | # set this value to 0 (zero). 86 | # 87 | {Credo.Check.Design.TagTODO, [exit_status: 2]}, 88 | {Credo.Check.Design.TagFIXME, []}, 89 | 90 | # 91 | ## Readability Checks 92 | # 93 | {Credo.Check.Readability.AliasAs, 94 | files: %{excluded: ["lib/*_web.ex", "test/support/conn_case.ex"]}}, 95 | {Credo.Check.Readability.AliasOrder, []}, 96 | {Credo.Check.Readability.BlockPipe, []}, 97 | {Credo.Check.Readability.FunctionNames, []}, 98 | {Credo.Check.Readability.ImplTrue, []}, 99 | {Credo.Check.Readability.LargeNumbers, []}, 100 | {Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 120}, 101 | {Credo.Check.Readability.ModuleAttributeNames, []}, 102 | {Credo.Check.Readability.ModuleDoc, []}, 103 | {Credo.Check.Readability.ModuleNames, []}, 104 | {Credo.Check.Readability.MultiAlias, []}, 105 | {Credo.Check.Readability.ParenthesesInCondition, []}, 106 | {Credo.Check.Readability.ParenthesesOnZeroArityDefs, []}, 107 | {Credo.Check.Readability.PipeIntoAnonymousFunctions, []}, 108 | {Credo.Check.Readability.PredicateFunctionNames, []}, 109 | {Credo.Check.Readability.PreferImplicitTry, []}, 110 | {Credo.Check.Readability.RedundantBlankLines, []}, 111 | {Credo.Check.Readability.Semicolons, []}, 112 | {Credo.Check.Readability.SinglePipe, []}, 113 | {Credo.Check.Readability.SpaceAfterCommas, []}, 114 | {Credo.Check.Readability.StrictModuleLayout, []}, 115 | {Credo.Check.Readability.StringSigils, []}, 116 | {Credo.Check.Readability.TrailingBlankLine, []}, 117 | {Credo.Check.Readability.TrailingWhiteSpace, []}, 118 | {Credo.Check.Readability.UnnecessaryAliasExpansion, []}, 119 | {Credo.Check.Readability.VariableNames, []}, 120 | {Credo.Check.Readability.WithCustomTaggedTuple, []}, 121 | {Credo.Check.Readability.WithSingleClause, []}, 122 | 123 | # 124 | ## Refactoring Opportunities 125 | # 126 | {Credo.Check.Refactor.Apply, []}, 127 | {Credo.Check.Refactor.CondStatements, []}, 128 | {Credo.Check.Refactor.CyclomaticComplexity, []}, 129 | {Credo.Check.Refactor.FilterFilter, []}, 130 | {Credo.Check.Refactor.FilterReject, []}, 131 | {Credo.Check.Refactor.FunctionArity, []}, 132 | {Credo.Check.Refactor.IoPuts, []}, 133 | {Credo.Check.Refactor.LongQuoteBlocks, []}, 134 | {Credo.Check.Refactor.MapJoin, []}, 135 | {Credo.Check.Refactor.MapMap, []}, 136 | {Credo.Check.Refactor.MatchInCondition, []}, 137 | {Credo.Check.Refactor.NegatedConditionsInUnless, []}, 138 | {Credo.Check.Refactor.NegatedConditionsWithElse, []}, 139 | {Credo.Check.Refactor.Nesting, []}, 140 | {Credo.Check.Refactor.PipeChainStart, []}, 141 | {Credo.Check.Refactor.RedundantWithClauseResult, []}, 142 | {Credo.Check.Refactor.RejectFilter, []}, 143 | {Credo.Check.Refactor.RejectReject, []}, 144 | {Credo.Check.Refactor.UnlessWithElse, []}, 145 | {Credo.Check.Refactor.WithClauses, []}, 146 | 147 | # 148 | ## Warnings 149 | # 150 | {Credo.Check.Warning.ApplicationConfigInModuleAttribute, []}, 151 | {Credo.Check.Warning.BoolOperationOnSameValues, []}, 152 | {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []}, 153 | {Credo.Check.Warning.IExPry, []}, 154 | {Credo.Check.Warning.IoInspect, []}, 155 | {Credo.Check.Warning.MapGetUnsafePass, []}, 156 | {Credo.Check.Warning.MixEnv, []}, 157 | {Credo.Check.Warning.OperationOnSameValues, []}, 158 | {Credo.Check.Warning.OperationWithConstantResult, []}, 159 | {Credo.Check.Warning.RaiseInsideRescue, []}, 160 | {Credo.Check.Warning.SpecWithStruct, []}, 161 | {Credo.Check.Warning.UnsafeExec, []}, 162 | {Credo.Check.Warning.UnsafeToAtom, []}, 163 | {Credo.Check.Warning.UnusedEnumOperation, []}, 164 | {Credo.Check.Warning.UnusedFileOperation, []}, 165 | {Credo.Check.Warning.UnusedKeywordOperation, []}, 166 | {Credo.Check.Warning.UnusedListOperation, []}, 167 | {Credo.Check.Warning.UnusedPathOperation, []}, 168 | {Credo.Check.Warning.UnusedRegexOperation, []}, 169 | {Credo.Check.Warning.UnusedStringOperation, []}, 170 | {Credo.Check.Warning.UnusedTupleOperation, []}, 171 | {Credo.Check.Warning.WrongTestFileExtension, []} 172 | ], 173 | disabled: [ 174 | # 175 | # Controversial and experimental checks (opt-in, just move the check to `:enabled` 176 | # and be sure to use `mix credo --strict` to see low priority checks if you set 177 | # `strict: false` above) 178 | # 179 | {Credo.Check.Consistency.MultiAliasImportRequireUse, []}, 180 | {Credo.Check.Consistency.UnusedVariableNames, []}, 181 | {Credo.Check.Design.DuplicatedCode, []}, 182 | {Credo.Check.Readability.SeparateAliasRequire, []}, 183 | {Credo.Check.Readability.SingleFunctionToBlockPipe, []}, 184 | {Credo.Check.Refactor.ABCSize, []}, 185 | {Credo.Check.Refactor.AppendSingleItem, []}, 186 | {Credo.Check.Refactor.DoubleBooleanNegation, []}, 187 | {Credo.Check.Refactor.ModuleDependencies, []}, 188 | {Credo.Check.Refactor.NegatedIsNil, []}, 189 | {Credo.Check.Refactor.VariableRebinding, []}, 190 | {Credo.Check.Warning.LeakyEnvironment, []}, 191 | {Credo.Check.Readability.Specs, 192 | files: %{ 193 | excluded: [ 194 | "lib/*_web.ex", 195 | "lib/*_web/controllers/*_controller.ex", 196 | "lib/*_web/graphql/*/resolvers.ex" 197 | ] 198 | }} 199 | 200 | # 201 | # Custom checks can be created using `mix credo.gen.check`. 202 | # 203 | ] 204 | } 205 | } 206 | ] 207 | } 208 | -------------------------------------------------------------------------------- /.dialyzer.ignore-warnings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/podium/simple_token_authentication/8d73ed71357d870f3216d08ec82ca2748ac5cd05/.dialyzer.ignore-warnings -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # add github group owner for uinta only 2 | * @podium/oss-engineers 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: epinault 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | ** Provide the following details 14 | 15 | - Elixir version (elixir -v): 16 | - Erlang version (erl -v): 17 | - Operating system: 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Actual behavior** 23 | A clear and concise description of what actually happens. 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | 9 | - package-ecosystem: "mix" 10 | directory: "/" 11 | schedule: 12 | interval: "weekly" -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | setup: 13 | runs-on: ${{ matrix.os }} 14 | env: 15 | MIX_ENV: test 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: [ubuntu-22.04] 21 | elixir_version: [1.15, 1.16, 1.17] 22 | otp_version: [ 25, 26, 27] 23 | exclude: 24 | - otp_version: 27 25 | elixir_version: 1.15 26 | - otp_version: 27 27 | elixir_version: 1.16 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | 32 | - name: Set up Elixir 33 | id: beam 34 | uses: erlef/setup-beam@v1 35 | with: 36 | otp-version: ${{matrix.otp_version}} 37 | elixir-version: ${{matrix.elixir_version}} 38 | 39 | - uses: actions/cache@v4 40 | with: 41 | path: | 42 | deps 43 | _build 44 | key: deps-${{ runner.os }}-${{ matrix.otp_version }}-${{ matrix.elixir_version }}-${{ hashFiles('**/mix.lock') }} 45 | restore-keys: | 46 | deps-${{ runner.os }}-${{ matrix.otp_version }}-${{ matrix.elixir_version }} 47 | 48 | - run: mix deps.get 49 | 50 | - run: mix deps.unlock --check-unused 51 | 52 | - run: mix deps.compile 53 | 54 | - run: mix compile --warnings-as-errors 55 | 56 | - run: mix credo --strict --format=oneline 57 | 58 | - run: mix test --warnings-as-errors --cover 59 | 60 | dialyzer: 61 | runs-on: ubuntu-22.04 62 | env: 63 | MIX_ENV: dev 64 | 65 | steps: 66 | - uses: actions/checkout@v4 67 | 68 | - name: Set up Elixir 69 | id: beam 70 | uses: erlef/setup-beam@v1 71 | with: 72 | elixir-version: 1.16 73 | otp-version: 26 74 | 75 | - uses: actions/cache@v4 76 | with: 77 | path: | 78 | deps 79 | _build 80 | key: deps-${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-${{ hashFiles('**/mix.lock') }} 81 | restore-keys: | 82 | deps-${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }} 83 | 84 | - run: mix deps.get 85 | 86 | - name: Restore PLT cache 87 | id: plt_cache_restore 88 | uses: actions/cache/restore@v4 89 | with: 90 | key: | 91 | plts-${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-${{ hashFiles('**/mix.lock') }} 92 | restore-keys: | 93 | plts-${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}- 94 | path: | 95 | priv/plts 96 | 97 | - name: Create PLTs 98 | if: steps.plt_cache_restore.outputs.cache-hit != 'true' 99 | run: mix dialyzer --plt 100 | 101 | - name: Save PLT cache 102 | id: plt_cache_save 103 | if: steps.plt_cache_restore.outputs.cache-hit != 'true' 104 | uses: actions/cache/save@v4 105 | with: 106 | key: | 107 | plts-${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-${{ hashFiles('**/mix.lock') }} 108 | path: | 109 | priv/plts 110 | 111 | - name: Run dialyzer 112 | run: mix dialyzer --format github --format dialyxir 113 | 114 | check_format: 115 | runs-on: ubuntu-22.04 116 | env: 117 | MIX_ENV: dev 118 | 119 | steps: 120 | - uses: actions/checkout@v4 121 | 122 | - name: Set up Elixir 123 | id: beam 124 | uses: erlef/setup-beam@v1 125 | with: 126 | elixir-version: 1.16 127 | otp-version: 26 128 | 129 | - uses: actions/cache@v4 130 | with: 131 | path: | 132 | deps 133 | _build 134 | key: deps-${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-${{ hashFiles('**/mix.lock') }} 135 | restore-keys: | 136 | deps-${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }} 137 | 138 | - run: mix deps.get 139 | 140 | - run: mix format --check-formatted 141 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check out 13 | uses: actions/checkout@v4 14 | 15 | - name: Publish package to hex.pm 16 | uses: hipcall/github_action_publish_hex@v1 17 | env: 18 | HEX_API_KEY: ${{ secrets.HEX_API_KEY }} 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # If the VM crashes, it generates a dump, let's ignore it too. 14 | erl_crash.dump 15 | 16 | # Also ignore archive artifacts (built via "mix archive.build"). 17 | *.ez 18 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | elixir 1.17.3 2 | erlang 26.2.5 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | 3 | ## [0.8.0] (2024-10-01) 4 | ### Changed 5 | * Remove support for Elixir 1.11, 1.12 and 1.13. Minimum is Elixir 1.14 6 | 7 | ## [0.7.0] (2022-11-28) 8 | ### Changed 9 | * Upgrade dependencies 10 | * Clean up some docs 11 | * some internal code cleaning 12 | * ** Breaking ** Support Elixir 1.11 and above 13 | ## [0.6.0] (2021-05-24) 14 | ### Changed 15 | * Upgrade dependencies 16 | * Adds the service name to the token identified into the conn under the key simple_token_auth_service 17 | * **Potentially Breaking** Depend on `persistent_term` for caching the config which requires OTP 21.2 minimum (http://erlang.org/doc/man/persistent_term.html) 18 | 19 | ## [0.5.0] (2020-07-07) 20 | ### Changed 21 | * Add supports for service specific token and log the service name in the metadata 22 | 23 | For prior versions, please look at the commit log 24 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. 6 | 7 | Examples of unacceptable behavior by participants include: 8 | 9 | * The use of sexualized language or imagery 10 | * Personal attacks 11 | * Trolling or insulting/derogatory comments 12 | * Public or private harassment 13 | * Publishing other's private information, such as physical or electronic addresses, without explicit permission 14 | * Other unethical or unprofessional conduct. 15 | 16 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. 17 | 18 | This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 21 | 22 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.2.0, available at [https://www.contributor-covenant.org/version/1/2/0/code-of-conduct/](https://www.contributor-covenant.org/version/1/2/0/code-of-conduct/) 23 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Simple Token Authentication 2 | 3 | Please take a moment to review this document in order to make the contribution 4 | process easy and effective for everyone involved! 5 | Also make sure you read our [Code of Conduct](CODE_OF_CONDUCT.md) that outlines our commitment towards an open and welcoming environment. 6 | 7 | ## Using the issue tracker 8 | 9 | Use the issues tracker for: 10 | 11 | * [Bug reports](#bug-reports) 12 | * [Submitting pull requests](#pull-requests) 13 | 14 | We do our best to keep the issue tracker tidy and organized, making it useful 15 | for everyone. For example, we classify open issues per perceived difficulty, 16 | making it easier for developers to [contribute to Simple Token Authentication](#pull-requests). 17 | 18 | ## Bug reports 19 | 20 | A bug is either a _demonstrable problem_ that is caused by the code in the repository, 21 | or indicate missing, unclear, or misleading documentation. Good bug reports are extremely 22 | helpful - thank you! 23 | 24 | Guidelines for bug reports: 25 | 26 | 1. **Use the GitHub issue search** — check if the issue has already been 27 | reported. 28 | 29 | 2. **Check if the issue has been fixed** — try to reproduce it using the 30 | `master` branch in the repository. 31 | 32 | 3. **Isolate and report the problem** — ideally create a reduced test 33 | case. 34 | 35 | Please try to be as detailed as possible in your report. Include information about 36 | your Operating System, as well as your Erlang, Elixir and Simple Token Authentication versions. Please provide steps to 37 | reproduce the issue as well as the outcome you were expecting! All these details 38 | will help developers to fix any potential bugs. 39 | 40 | Example: 41 | 42 | > Short and descriptive example bug report title 43 | > 44 | > A summary of the issue and the environment in which it occurs. If suitable, 45 | > include the steps required to reproduce the bug. 46 | > 47 | > 1. This is the first step 48 | > 2. This is the second step 49 | > 3. Further steps, etc. 50 | > 51 | > `` - a link to the reduced test case (e.g. a GitHub Gist) 52 | > 53 | > Any other information you want to share that is relevant to the issue being 54 | > reported. This might include the lines of code that you have identified as 55 | > causing the bug, and potential solutions (and your opinions on their 56 | > merits). 57 | 58 | ## Contributing Documentation 59 | 60 | Code documentation (`@doc`, `@moduledoc`, `@typedoc`) has a special convention: 61 | the first paragraph is considered to be a short summary. 62 | 63 | For functions, macros and callbacks say what it will do. For example write 64 | something like: 65 | 66 | ```elixir 67 | @doc """ 68 | Marks the given value as HTML safe. 69 | """ 70 | def safe({:safe, value}), do: {:safe, value} 71 | ``` 72 | 73 | For modules, protocols and types say what it is. For example write 74 | something like: 75 | 76 | ```elixir 77 | defmodule MyModule do 78 | @moduledoc """ 79 | Conveniences for working HTML strings and templates. 80 | ... 81 | """ 82 | ``` 83 | 84 | Keep in mind that the first paragraph might show up in a summary somewhere, long 85 | texts in the first paragraph create very ugly summaries. As a rule of thumb 86 | anything longer than 80 characters is too long. 87 | 88 | Try to keep unnecessary details out of the first paragraph, it's only there to 89 | give a user a quick idea of what the documented "thing" does/is. The rest of the 90 | documentation string can contain the details, for example when a value and when 91 | `nil` is returned. 92 | 93 | If possible include examples, preferably in a form that works with doctests. 94 | This makes it easy to test the examples so that they don't go stale and examples 95 | are often a great help in explaining what a function does. 96 | 97 | ## Pull requests 98 | 99 | Good pull requests - patches, improvements, new features - are a fantastic 100 | help. They should remain focused in scope and avoid containing unrelated 101 | commits. 102 | 103 | **IMPORTANT**: By submitting a patch, you agree that your work will be 104 | licensed under the license used by the project. 105 | 106 | If you have any large pull request in mind (e.g. implementing features, 107 | refactoring code, etc), **please ask first** otherwise you risk spending 108 | a lot of time working on something that the project's developers might 109 | not want to merge into the project. 110 | 111 | Please adhere to the coding conventions in the project (indentation, 112 | accurate comments, etc.) and don't forget to add your own tests and 113 | documentation. When working with git, we recommend the following process 114 | in order to craft an excellent pull request: 115 | 116 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your fork, 117 | and configure the remotes: 118 | 119 | ```bash 120 | # Clone your fork of the repo into the current directory 121 | git clone https://github.com//simple_token_authentication 122 | 123 | # Navigate to the newly cloned directory 124 | cd simple_token_authentication 125 | 126 | # Assign the original repo to a remote called "upstream" 127 | git remote add upstream https://github.com/podium/simple_token_authentication 128 | ``` 129 | 130 | 2. If you cloned a while ago, get the latest changes from upstream, and update your fork: 131 | 132 | ```bash 133 | git checkout master 134 | git pull upstream master 135 | git push 136 | ``` 137 | 138 | 3. Create a new topic branch (off of `master`) to contain your feature, change, 139 | or fix. 140 | 141 | **IMPORTANT**: Making changes in `master` is discouraged. You should always 142 | keep your local `master` in sync with upstream `master` and make your 143 | changes in topic branches. 144 | 145 | ```bash 146 | git checkout -b 147 | ``` 148 | 149 | 4. Commit your changes in logical chunks. Keep your commit messages organized, 150 | with a short description in the first line and more detailed information on 151 | the following lines. Feel free to use Git's 152 | [interactive rebase](https://help.github.com/articles/about-git-rebase/) 153 | feature to tidy up your commits before making them public. 154 | 155 | 5. Make sure all the tests are still passing. 156 | 157 | ```bash 158 | mix test 159 | ``` 160 | 161 | 6. Push your topic branch up to your fork: 162 | 163 | ```bash 164 | git push origin 165 | ``` 166 | 167 | 7. [Open a Pull Request](https://help.github.com/articles/about-pull-requests/) 168 | with a clear title and description. 169 | 170 | 8. If you haven't updated your pull request for a while, you should consider 171 | rebasing on master and resolving any conflicts. 172 | 173 | **IMPORTANT**: _Never ever_ merge upstream `master` into your branches. You 174 | should always `git rebase` on `master` to bring your changes up to date when 175 | necessary. 176 | 177 | ```bash 178 | git checkout master 179 | git pull upstream master 180 | git checkout 181 | git rebase master 182 | ``` 183 | 184 | Thank you for your contributions! 185 | 186 | ## Guides 187 | 188 | These Guides aim to be inclusive. We use "we" and "our" instead of "you" and 189 | "your" to foster this sense of inclusion. 190 | 191 | Ideally there is something for everybody in each guide, from beginner to expert. 192 | This is hard, maybe impossible. When we need to compromise, we do so on behalf 193 | of beginning users because expert users have more tools at their disposal to 194 | help themselves. 195 | 196 | The general pattern we use for presenting information is to first introduce a 197 | small, discrete topic, then write a small amount of code to demonstrate the 198 | concept, then verify that the code worked. 199 | 200 | In this way, we build from small, easily digestible concepts into more complex 201 | ones. The shorter this cycle is, as long as the information is still clear and 202 | complete, the better. 203 | 204 | For formatting the guides: 205 | 206 | - We use the `elixir` code fence for all module code. 207 | - We use the `iex` for IEx sessions. 208 | - We use the `console` code fence for shell commands. 209 | - We use the `html` code fence for html templates, even if there is elixir code 210 | in the template. 211 | - We use backticks for filenames and directory paths. 212 | - We use backticks for module names, function names, and variable names. 213 | - Documentation line length should hard wrapped at around 100 characters if possible. 214 | -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | Credits in no special order: 2 | 3 | - [Arthur Weagel](https://github.com/aweagel) 4 | - [Travis Elnicky](https://github.com/telnicky) 5 | - [Trevor Fenn](https://github.com/sgtpepper43) 6 | - [Emmanuel Pinault](https://github.com/epinault) 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Podium 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleTokenAuthentication 2 | 3 | [![Build Status](https://github.com/podium/simple_token_authentication/actions/workflows/ci.yml/badge.svg)](https://github.com/podium/simple_token_authentication/actions/workflows/ci.yml) [![Hex.pm](https://img.shields.io/hexpm/v/simple_token_authentication.svg)](https://hex.pm/packages/simple_token_authentication) [![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/simple_token_authentication) 4 | [![Total Download](https://img.shields.io/hexpm/dt/simple_token_authentication.svg)](https://hex.pm/packages/simple_token_authentication) 5 | [![License](https://img.shields.io/hexpm/l/simple_token_authentication.svg)](https://github.com/podium/simple_token_authentication/blob/master/LICENSE.md) 6 | ## Usage 7 | ### Phoenix Integration 8 | 9 | Inside `web/router.ex` file, add plug to your pipeline like so: 10 | 11 | ```elixir 12 | defmodule MyApp.Router 13 | use Phoenix.Router 14 | 15 | pipeline :api do 16 | plug SimpleTokenAuthentication 17 | end 18 | 19 | scope "/", MyApp do 20 | pipe_through :api 21 | get "/hello", HelloController, :hello 22 | end 23 | end 24 | ``` 25 | ## Installation 26 | 27 | 1. Add `simple_token_authentication` to your list of dependencies in `mix.exs`: 28 | 29 | ```elixir 30 | def deps do 31 | [{:simple_token_authentication, "~> 0.6.0"}] 32 | end 33 | ``` 34 | 35 | 2. Ensure `simple_token_authentication` is started before your application: 36 | 37 | ```elixir 38 | def application do 39 | [applications: [:simple_token_authentication]] 40 | end 41 | ``` 42 | 43 | 3. Configure your token in `config.exs`: 44 | ```elixir 45 | config :simple_token_authentication, 46 | token: "your-token-here", 47 | service_tokens: [ 48 | service_a: "service-a-token", 49 | service_b: "service-b-token" 50 | ] 51 | ``` 52 | 53 | 4. Configure your connecting application to pass a token in the `authorization` header, e.g.: 54 | ```elixir 55 | put_header("authorization", "your-token-here") 56 | ``` 57 | 58 | ## Notes 59 | 60 | - Token value can be a comma-separated list of tokens 61 | - Specifying `service_tokens` is optional 62 | - Auth will succeed if token exists in *either* list (`token` or `service_tokens`) 63 | - Use of a service token will add "service_name" to `Logging.metadata` 64 | - Service can be identified in the conn.assigns[:simple_token_auth_service]. Will be the name of the service or :global when matching the token key 65 | 66 | ## Copyright and License 67 | 68 | Copyright (c) 2016 Podium 69 | 70 | This work is free. You can redistribute it and/or modify it under the 71 | terms of the MIT License. See the [LICENSE.md](./LICENSE.md) file for more details. -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Instructions 2 | 3 | 1. Check related deps for required version bumps and compatibility 4 | 2. Bump version in related files below 5 | 3. Bump external dependency version in related external files below 6 | 4. Run tests: 7 | - `mix test` in the root folder 8 | - `mix credo` in the root folder 9 | 5. Commit, push code 10 | 6. Publish `simple_token_authentication` packages and docs 11 | 12 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | import Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure for your application as: 12 | # 13 | # config :simple_token_authentication, key: :value 14 | # 15 | # And access this configuration in your application as: 16 | # 17 | # Application.get_env(:simple_token_authentication, :key) 18 | # 19 | # Or configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | config :logger, :console, 25 | format: "\n$time $metadata [$level] $message", 26 | metadata: [:service_name] 27 | 28 | # It is also possible to import configuration files, relative to this 29 | # directory. For example, you can emulate configuration per environment 30 | # by uncommenting the line below and defining dev.exs, test.exs and such. 31 | # Configuration from the imported file will override the ones defined 32 | # here (which is why it is important to import them last). 33 | # 34 | # import_config "#{Mix.env}.exs" 35 | -------------------------------------------------------------------------------- /guides/config.md: -------------------------------------------------------------------------------- 1 | ## Local Development configuration 2 | 3 | This section describes the available configuration when working with this library 4 | 5 | -------------------------------------------------------------------------------- /lib/simple_token_authentication.ex: -------------------------------------------------------------------------------- 1 | defmodule SimpleTokenAuthentication do 2 | @moduledoc """ 3 | A plug that checks for presence of a simple token for authentication 4 | """ 5 | @behaviour Plug 6 | 7 | import Plug.Conn 8 | require Logger 9 | 10 | def init(opts), do: opts 11 | 12 | def call(conn, _opts) do 13 | val = get_auth_header(conn) 14 | 15 | token_map = 16 | case :persistent_term.get(:simple_token_authentication_map, nil) do 17 | nil -> 18 | build_token_map() 19 | 20 | val -> 21 | val 22 | end 23 | 24 | if service_name = token_map[val] do 25 | Logger.metadata(service_name: service_name) 26 | assign(conn, :simple_token_auth_service, service_name) 27 | else 28 | conn 29 | |> put_resp_content_type("application/json") 30 | |> send_resp(401, ~s({ "error": "Invalid shared key" })) 31 | |> halt() 32 | end 33 | end 34 | 35 | defp get_auth_header(conn) do 36 | case get_req_header(conn, "authorization") do 37 | [val | _] -> val 38 | _ -> "" 39 | end 40 | end 41 | 42 | def build_token_map do 43 | global = 44 | :simple_token_authentication 45 | |> Application.get_env(:token) 46 | |> List.wrap() 47 | 48 | services = 49 | :simple_token_authentication 50 | |> Application.get_env(:service_tokens) 51 | |> List.wrap() 52 | 53 | token_map = 54 | for {service, tokens} <- [{:global, global} | services], 55 | token <- List.wrap(tokens), 56 | token != "", 57 | reduce: %{} do 58 | acc -> 59 | Map.put(acc, token, service) 60 | end 61 | 62 | :persistent_term.put(:simple_token_authentication_map, token_map) 63 | 64 | token_map 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule SimpleTokenAuthentication.Mixfile do 2 | use Mix.Project 3 | 4 | @source_url "https://github.com/podium/simple_token_authentication" 5 | @version "0.8.0" 6 | 7 | def project do 8 | [ 9 | app: :simple_token_authentication, 10 | version: @version, 11 | elixir: "~> 1.14", 12 | description: "A plug that checks for presence of a simple token for authentication", 13 | build_embedded: Mix.env() == :prod, 14 | start_permanent: Mix.env() == :prod, 15 | deps: deps(), 16 | dialyzer: [ 17 | ignore_warnings: ".dialyzer.ignore-warnings", 18 | list_unused_filters: true, 19 | plt_add_apps: [:mix] 20 | ], 21 | docs: docs(), 22 | package: package(), 23 | test_coverage: [summary: [threshold: 90]] 24 | ] 25 | end 26 | 27 | def application do 28 | [extra_applications: [:logger, :plug]] 29 | end 30 | 31 | defp deps do 32 | [ 33 | {:credo, "~> 1.6", only: [:dev, :test]}, 34 | {:dialyxir, "~> 1.4", only: :dev, runtime: false}, 35 | {:ex_doc, "~> 0.28", only: :dev, runtime: false}, 36 | {:plug, ">= 1.3.0"} 37 | ] 38 | end 39 | 40 | defp docs do 41 | [ 42 | main: "readme", 43 | extras: [ 44 | "CHANGELOG.md", 45 | {:"README.md", title: "Readme"} 46 | ], 47 | source_url: @source_url, 48 | source_ref: "v#{@version}", 49 | homepage_url: @source_url 50 | ] 51 | end 52 | 53 | defp package do 54 | [ 55 | name: :simple_token_authentication, 56 | files: ["lib", "mix.exs", "README*"], 57 | licenses: ["MIT"], 58 | links: %{ 59 | "GitHub" => "https://github.com/podium/simple_token_authentication", 60 | "Docs" => "https://hexdocs.pm/simple_token_authentication/#{@version}/", 61 | "Changelog" => "#{@source_url}/blob/master/CHANGELOG.md" 62 | } 63 | ] 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, 3 | "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, 4 | "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, 5 | "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, 6 | "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, 7 | "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, 8 | "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, 9 | "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, 10 | "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, 11 | "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, 12 | "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, 13 | "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, 14 | "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, 15 | "plug": {:hex, :plug, "1.18.0", "d78df36c41f7e798f2edf1f33e1727eae438e9dd5d809a9997c463a108244042", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "819f9e176d51e44dc38132e132fe0accaf6767eab7f0303431e404da8476cfa2"}, 16 | "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, 17 | "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, 18 | } 19 | -------------------------------------------------------------------------------- /test/simple_token_authentication_test.exs: -------------------------------------------------------------------------------- 1 | defmodule SimpleTokenAuthenticationTest do 2 | use ExUnit.Case, async: false 3 | import Plug.Test 4 | import Plug.Conn 5 | import ExUnit.CaptureLog 6 | require Logger 7 | 8 | @opts SimpleTokenAuthentication.init([]) 9 | 10 | defmacro with_token(token, do: expression) do 11 | quote do 12 | Application.put_env(:simple_token_authentication, :token, unquote(token)) 13 | unquote(expression) 14 | Application.put_env(:simple_token_authentication, :token, nil) 15 | end 16 | end 17 | 18 | defmacro with_service_tokens(tokens, do: expression) do 19 | quote do 20 | Application.put_env(:simple_token_authentication, :service_tokens, unquote(tokens)) 21 | unquote(expression) 22 | Application.put_env(:simple_token_authentication, :service_tokens, nil) 23 | end 24 | end 25 | 26 | setup do 27 | :persistent_term.erase(:simple_token_authentication_map) 28 | :ok 29 | end 30 | 31 | describe "without a token" do 32 | test "returns a 401 status code" do 33 | with_token(nil) do 34 | # Create a test connection 35 | conn = conn(:get, "/foo") 36 | 37 | # Invoke the plug 38 | conn = SimpleTokenAuthentication.call(conn, @opts) 39 | 40 | # Assert the response and status 41 | assert conn.status == 401 42 | end 43 | end 44 | end 45 | 46 | test "handles the lack of an auth header" do 47 | with_token("fake_token") do 48 | conn = 49 | :get 50 | |> conn("/foo") 51 | |> SimpleTokenAuthentication.call(@opts) 52 | 53 | assert conn.status == 401 54 | end 55 | end 56 | 57 | describe "empty token" do 58 | test "returns a 401 status code" do 59 | with_token("") do 60 | # Create a test connection 61 | conn = 62 | :get 63 | |> conn("/foo") 64 | |> put_req_header("authorization", "") 65 | 66 | # Invoke the plug 67 | conn = SimpleTokenAuthentication.call(conn, @opts) 68 | 69 | # Assert the response and status 70 | assert conn.status == 401 71 | assert conn.assigns[:simple_token_auth_service] == nil 72 | end 73 | end 74 | end 75 | 76 | describe "with an invalid token" do 77 | test "returns a 401 status code" do 78 | with_token("fake_token") do 79 | # Create a test connection 80 | conn = 81 | :get 82 | |> conn("/foo") 83 | |> put_req_header("authorization", "wrong_token") 84 | 85 | # Invoke the plug 86 | conn = SimpleTokenAuthentication.call(conn, @opts) 87 | 88 | # Assert the response and status 89 | assert conn.status == 401 90 | end 91 | end 92 | end 93 | 94 | describe "with a valid token" do 95 | test "returns a 200 status code" do 96 | with_token("fake_token") do 97 | # Create a test connection 98 | conn = 99 | :get 100 | |> conn("/foo") 101 | |> put_req_header("authorization", "fake_token") 102 | 103 | # Invoke the plug 104 | conn = SimpleTokenAuthentication.call(conn, @opts) 105 | 106 | # Assert the response and status 107 | assert conn.status != 401 108 | assert conn.assigns[:simple_token_auth_service] == :global 109 | end 110 | end 111 | end 112 | 113 | describe "with multiple tokens" do 114 | test "returns a 200 status code if one of the tokens matches" do 115 | with_token(["bad_token", "fake_token"]) do 116 | # Create a test connection 117 | conn = 118 | :get 119 | |> conn("/foo") 120 | |> put_req_header("authorization", "fake_token") 121 | 122 | # Invoke the plug 123 | conn = SimpleTokenAuthentication.call(conn, @opts) 124 | 125 | # Assert the response and status 126 | assert conn.status != 401 127 | assert conn.assigns[:simple_token_auth_service] == :global 128 | end 129 | end 130 | 131 | test "returns a 401 status code if none of the tokens matches" do 132 | with_token(["bad_token", "other_bad_token"]) do 133 | # Create a test connection 134 | conn = 135 | :get 136 | |> conn("/foo") 137 | |> put_req_header("authorization", "fake_token") 138 | 139 | # Invoke the plug 140 | conn = SimpleTokenAuthentication.call(conn, @opts) 141 | 142 | # Assert the response and status 143 | assert conn.status == 401 144 | assert conn.assigns[:simple_token_auth_service] == nil 145 | end 146 | end 147 | end 148 | 149 | describe "with service tokens" do 150 | test "logs the service name" do 151 | with_service_tokens(test_service: "test_token") do 152 | # Create a test connection 153 | conn = 154 | :get 155 | |> conn("/foo") 156 | |> put_req_header("authorization", "test_token") 157 | 158 | # Invoke the plug 159 | conn = SimpleTokenAuthentication.call(conn, @opts) 160 | 161 | assert capture_log(fn -> 162 | Logger.info("test log message") 163 | end) =~ "service_name=test_service" 164 | 165 | assert conn.assigns[:simple_token_auth_service] == :test_service 166 | end 167 | end 168 | 169 | test "returns a 401 status code if none of the service tokens matches" do 170 | with_service_tokens(bad: "bad_token", other: "other_bad_token") do 171 | # Create a test connection 172 | conn = 173 | :get 174 | |> conn("/foo") 175 | |> put_req_header("authorization", "fake_token") 176 | 177 | # Invoke the plug 178 | conn = SimpleTokenAuthentication.call(conn, @opts) 179 | 180 | # Assert the response and status 181 | assert conn.status == 401 182 | assert conn.assigns[:simple_token_auth_service] == nil 183 | end 184 | end 185 | end 186 | end 187 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------