├── .clang-format ├── .clang-tidy ├── .cmake-format ├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── feature-request.yml │ └── support-question.yml ├── actions │ ├── badge │ │ ├── action.yml │ │ ├── publish │ │ │ └── action.yml │ │ └── write │ │ │ └── action.yml │ ├── install │ │ ├── boost-json │ │ │ └── action.yml │ │ ├── cmake │ │ │ └── action.yml │ │ ├── danielaparker-jsoncons │ │ │ └── action.yml │ │ ├── gtest │ │ │ └── action.yml │ │ ├── kazuho-picojson │ │ │ └── action.yml │ │ ├── libressl │ │ │ └── action.yml │ │ ├── nlohmann-json │ │ │ └── action.yml │ │ ├── open-source-parsers-jsoncpp │ │ │ └── action.yml │ │ ├── openssl │ │ │ └── action.yml │ │ └── wolfssl │ │ │ └── action.yml │ ├── process-linting-results │ │ └── action.yml │ └── render │ │ ├── defaults │ │ └── action.yml │ │ └── tests │ │ └── action.yml ├── logo.svg ├── security.md └── workflows │ ├── cmake.yml │ ├── cross-platform.yml │ ├── documentation.yml │ ├── jwt.yml │ ├── lint.yml │ ├── release.yml │ ├── ssl.yml │ ├── targets.yml │ └── traits.yml ├── .gitignore ├── .vscode └── extensions.json ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── cmake ├── CMakePresets.json ├── HunterGate.cmake ├── code-coverage.cmake ├── jwt-cpp-config.cmake.in ├── private-find-boost-json.cmake └── private-find-kazuho-picojson.cmake ├── docs ├── .gitignore ├── CMakeLists.txt ├── Doxyfile ├── faqs.md ├── install.md ├── overrides.css ├── signing.md ├── ssl.md └── traits.md ├── example ├── CMakeLists.txt ├── CMakePresets.json ├── es256k.cpp ├── jwks-verify.cpp ├── partial-claim-verifier.cpp ├── print-claims.cpp ├── private-claims.cpp ├── rsa-create.cpp ├── rsa-verify.cpp └── traits │ ├── CMakeLists.txt │ ├── README.md │ ├── boost-json.cpp │ ├── danielaparker-jsoncons.cpp │ ├── kazuho-picojson.cpp │ ├── nlohmann-json.cpp │ └── open-source-parsers-jsoncpp.cpp ├── include ├── jwt-cpp │ ├── base.h │ ├── jwt.h │ └── traits │ │ ├── boost-json │ │ ├── defaults.h │ │ └── traits.h │ │ ├── danielaparker-jsoncons │ │ ├── defaults.h │ │ └── traits.h │ │ ├── defaults.h.mustache │ │ ├── kazuho-picojson │ │ ├── defaults.h │ │ └── traits.h │ │ ├── nlohmann-json │ │ ├── defaults.h │ │ └── traits.h │ │ └── open-source-parsers-jsoncpp │ │ ├── defaults.h │ │ └── traits.h └── picojson │ └── picojson.h ├── nuget ├── jwt-cpp.nuspec └── jwt-cpp.targets └── tests ├── BaseTest.cpp ├── CMakeLists.txt ├── CMakePresets.json ├── ClaimTest.cpp ├── HelperTest.cpp ├── JwksTest.cpp ├── Keys.cpp ├── OpenSSLErrorTest.cpp ├── TestMain.cpp ├── TokenFormatTest.cpp ├── TokenTest.cpp ├── cmake ├── CMakeLists.txt ├── base64-is-disabled.cpp ├── defaults-enabled.cpp ├── libressl-is-used.cpp ├── picojson-is-disabled.cpp └── wolfssl-is-used.cpp ├── fuzz ├── BaseDecodeFuzz.cpp ├── BaseEncodeFuzz.cpp ├── CMakeLists.txt ├── TokenDecodeFuzz.cpp ├── decode-corpus │ ├── 086a3aa337038cac8a75a05131444f222e48aee8 │ ├── 8ebaef2304e91465585c8d7fcf4d9f939e08d6b4 │ ├── ba528234d9f6949ed9c9626c08a782f6e7c15b8b │ ├── de1028a3fe87471f027522c3ed9ec02b8364a006 │ └── e8f531caaa67cecb1c7b162f3e1d4e320d79befd └── token-corpus │ ├── 9d891e731f75deae56884d79e9816736b7488080 │ ├── ff384e2421a333cd52f259cec14c7f790d595db9 │ └── valid-sample └── traits ├── BoostJsonTest.cpp ├── JsonconsTest.cpp ├── NlohmannTest.cpp ├── OspJsoncppTest.cpp └── TraitsTest.cpp.mustache /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | BreakBeforeBraces: Attach 3 | 4 | ColumnLimit: 120 # Match GitHub UI 5 | 6 | UseTab: Always 7 | TabWidth: 4 8 | IndentWidth: 4 9 | AccessModifierOffset: -4 10 | ContinuationIndentWidth: 4 11 | NamespaceIndentation: All 12 | IndentCaseLabels: false 13 | 14 | PointerAlignment: Left 15 | AlwaysBreakTemplateDeclarations: Yes 16 | SpaceAfterTemplateKeyword: false 17 | AllowShortCaseLabelsOnASingleLine: true 18 | AllowShortIfStatementsOnASingleLine: WithoutElse 19 | AllowShortBlocksOnASingleLine: Always 20 | 21 | FixNamespaceComments: true 22 | ReflowComments: false 23 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: '-*, 2 | bugprone-*, 3 | cert-*, 4 | clang-analyzer-*, 5 | clang-diagnostic-*, 6 | -clang-diagnostic-c++17-extensions, 7 | google-*, 8 | -google-runtime-references, 9 | -google-readability-braces-around-statements, 10 | hicpp-*, 11 | -hicpp-braces-around-statements, 12 | -hicpp-signed-bitwise, 13 | misc-*, 14 | -misc-non-private-member-variables-in-classes, 15 | llvm-*, 16 | modernize-*, 17 | -modernize-use-trailing-return-type, 18 | performance-*, 19 | portability-*, 20 | readability-*, 21 | -readability-magic-numbers, 22 | -readability-braces-around-statements, 23 | -readability-uppercase-literal-suffix, 24 | -misc-include-cleaner' 25 | 26 | CheckOptions: 27 | - key: readability-identifier-naming.TypedefCase 28 | value: lower_case 29 | - key: readability-identifier-naming.StructCase 30 | value: lower_case 31 | - key: readability-identifier-naming.ClassCase 32 | value: lower_case 33 | - key: readability-identifier-naming.VariableCase 34 | value: lower_case 35 | - key: readability-identifier-naming.ParameterCase 36 | value: lower_case 37 | - key: readability-identifier-naming.FunctionCase 38 | value: lower_case 39 | - key: readability-identifier-naming.NamespaceCase 40 | value: lower_case 41 | - key: readability-identifier-naming.GlobalConstantCase 42 | value: lower_case -------------------------------------------------------------------------------- /.cmake-format: -------------------------------------------------------------------------------- 1 | # ---------------------------------- 2 | # Options affecting listfile parsing 3 | # ---------------------------------- 4 | with section("parse"): 5 | 6 | # Specify structure for custom cmake functions 7 | additional_commands = { 'foo': { 'flags': ['BAR', 'BAZ'], 8 | 'kwargs': {'DEPENDS': '*', 'HEADERS': '*', 'SOURCES': '*'}}} 9 | 10 | # ----------------------------- 11 | # Options effecting formatting. 12 | # ----------------------------- 13 | with section("format"): 14 | 15 | # How wide to allow formatted cmake files 16 | line_width = 120 17 | 18 | # How many spaces to tab for indent 19 | tab_size = 2 20 | 21 | # If an argument group contains more than this many sub-groups (parg or kwarg 22 | # groups) then force it to a vertical layout. 23 | max_subgroups_hwrap = 12 24 | 25 | # If a positional argument group contains more than this many arguments, then 26 | # force it to a vertical layout. 27 | max_pargs_hwrap = 24 28 | 29 | # If true, separate flow control names from their parentheses with a space 30 | separate_ctrl_name_with_space = False 31 | 32 | # If true, separate function names from parentheses with a space 33 | separate_fn_name_with_space = False 34 | 35 | # If a statement is wrapped to more than one line, than dangle the closing 36 | # parenthesis on its own line. 37 | dangle_parens = False 38 | 39 | # If the trailing parenthesis must be 'dangled' on its on line, then align it 40 | # to this reference: `prefix`: the start of the statement, `prefix-indent`: 41 | # the start of the statement, plus one indentation level, `child`: align to 42 | # the column of the arguments 43 | dangle_align = 'prefix' 44 | 45 | # If the statement spelling length (including space and parenthesis) is 46 | # smaller than this amount, then force reject nested layouts. 47 | min_prefix_chars = 4 48 | 49 | # If the statement spelling length (including space and parenthesis) is larger 50 | # than the tab width by more than this amount, then force reject un-nested 51 | # layouts. 52 | max_prefix_chars = 10 53 | 54 | # If a candidate layout is wrapped horizontally but it exceeds this many 55 | # lines, then reject the layout. 56 | max_lines_hwrap = 12 57 | 58 | # What style line endings to use in the output. 59 | line_ending = 'unix' 60 | 61 | # Format command names consistently as 'lower' or 'upper' case 62 | command_case = 'lower' 63 | 64 | # Format keywords consistently as 'lower' or 'upper' case 65 | keyword_case = 'upper' 66 | 67 | # A list of command names which should always be wrapped 68 | always_wrap = [] 69 | 70 | # If true, the argument lists which are known to be sortable will be sorted 71 | # lexicographicall 72 | enable_sort = True 73 | 74 | # If true, the parsers may infer whether or not an argument list is sortable 75 | # (without annotation). 76 | autosort = False 77 | 78 | # By default, if cmake-format cannot successfully fit everything into the 79 | # desired linewidth it will apply the last, most agressive attempt that it 80 | # made. If this flag is True, however, cmake-format will print error, exit 81 | # with non-zero status code, and write-out nothing 82 | require_valid_layout = False 83 | 84 | # A dictionary mapping layout nodes to a list of wrap decisions. See the 85 | # documentation for more information. 86 | layout_passes = {} 87 | 88 | # ------------------------------------------------ 89 | # Options affecting comment reflow and formatting. 90 | # ------------------------------------------------ 91 | with section("markup"): 92 | 93 | # What character to use for bulleted lists 94 | bullet_char = '*' 95 | 96 | # What character to use as punctuation after numerals in an enumerated list 97 | enum_char = '.' 98 | 99 | # If comment markup is enabled, don't reflow the first comment block in each 100 | # listfile. Use this to preserve formatting of your copyright/license 101 | # statements. 102 | first_comment_is_literal = False 103 | 104 | # If comment markup is enabled, don't reflow any comment block which matches 105 | # this (regex) pattern. Default is `None` (disabled). 106 | literal_comment_pattern = None 107 | 108 | # Regular expression to match preformat fences in comments 109 | # default=r'^\s*([`~]{3}[`~]*)(.*)$' 110 | fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$' 111 | 112 | # Regular expression to match rulers in comments 113 | # default=r'^\s*[^\w\s]{3}.*[^\w\s]{3}$' 114 | ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$' 115 | 116 | # If a comment line matches starts with this pattern then it is explicitly a 117 | # trailing comment for the preceeding argument. Default is '#<' 118 | explicit_trailing_pattern = '#<' 119 | 120 | # If a comment line starts with at least this many consecutive hash 121 | # characters, then don't lstrip() them off. This allows for lazy hash rulers 122 | # where the first hash char is not separated by space 123 | hashruler_min_length = 10 124 | 125 | # If true, then insert a space between the first hash char and remaining hash 126 | # chars in a hash ruler, and normalize its length to fill the column 127 | canonicalize_hashrulers = True 128 | 129 | # enable comment markup parsing and reflow 130 | enable_markup = True 131 | 132 | # ---------------------------- 133 | # Options affecting the linter 134 | # ---------------------------- 135 | with section("lint"): 136 | 137 | # a list of lint codes to disable 138 | disabled_codes = [] 139 | 140 | # regular expression pattern describing valid function names 141 | function_pattern = '[0-9a-z_]+' 142 | 143 | # regular expression pattern describing valid macro names 144 | macro_pattern = '[0-9A-Z_]+' 145 | 146 | # regular expression pattern describing valid names for variables with global 147 | # scope 148 | global_var_pattern = '[0-9A-Z][0-9A-Z_]+' 149 | 150 | # regular expression pattern describing valid names for variables with global 151 | # scope (but internal semantic) 152 | internal_var_pattern = '_[0-9A-Z][0-9A-Z_]+' 153 | 154 | # regular expression pattern describing valid names for variables with local 155 | # scope 156 | local_var_pattern = '[0-9a-z_]+' 157 | 158 | # regular expression pattern describing valid names for privatedirectory 159 | # variables 160 | private_var_pattern = '_[0-9a-z_]+' 161 | 162 | # regular expression pattern describing valid names for publicdirectory 163 | # variables 164 | public_var_pattern = '[0-9A-Z][0-9A-Z_]+' 165 | 166 | # regular expression pattern describing valid names for keywords used in 167 | # functions or macros 168 | keyword_pattern = '[0-9A-Z_]+' 169 | 170 | # In the heuristic for C0201, how many conditionals to match within a loop in 171 | # before considering the loop a parser. 172 | max_conditionals_custom_parser = 2 173 | 174 | # Require at least this many newlines between statements 175 | min_statement_spacing = 1 176 | 177 | # Require no more than this many newlines between statements 178 | max_statement_spacing = 1 179 | max_returns = 6 180 | max_branches = 12 181 | max_arguments = 5 182 | max_localvars = 15 183 | max_statements = 50 184 | 185 | # ------------------------------- 186 | # Options effecting file encoding 187 | # ------------------------------- 188 | with section("encode"): 189 | 190 | # If true, emit the unicode byte-order mark (BOM) at the start of the file 191 | emit_byteorder_mark = False 192 | 193 | # Specify the encoding of the input file. Defaults to utf-8 194 | input_encoding = 'utf-8' 195 | 196 | # Specify the encoding of the output file. Defaults to utf-8. Note that cmake 197 | # only claims to support utf-8 so be careful when using anything else 198 | output_encoding = 'utf-8' 199 | 200 | # ------------------------------------- 201 | # Miscellaneous configurations options. 202 | # ------------------------------------- 203 | with section("misc"): 204 | 205 | # A dictionary containing any per-command configuration overrides. Currently 206 | # only `command_case` is supported. 207 | per_command = {} 208 | 209 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [!*.{h,cpp}] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{h,cpp}] 12 | indent_style = tab 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [Thalhammer,prince-chrismc] 2 | patreon: Thalhammer 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 🐛 2 | description: File a bug report 3 | 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | validations: 11 | required: false 12 | - type: textarea 13 | id: what-happened 14 | attributes: 15 | label: What happened? 16 | description: Also tell us, what did you expect to happen? Feel free to include some screenshots 17 | placeholder: Tell us what you see! 18 | value: "A bug happened!" 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: reproduce 23 | attributes: 24 | label: How To Reproduce? 25 | description: Please provide a small snippet to reproduce the issue 26 | placeholder: Some C++ code or Shell code to recreate th problem 27 | value: | 28 | ```c++ 29 | #include "jwt-cpp/jwt.h" 30 | int main() { 31 | return 0; 32 | } 33 | ``` 34 | - type: dropdown 35 | id: version 36 | attributes: 37 | label: Version 38 | description: What version of our software are you running? 39 | options: 40 | - 0.7.1 41 | - 0.7.0 42 | - 0.6.0 43 | - 0.5.2 44 | - Older (please let us know if the "What happened" box) 45 | validations: 46 | required: true 47 | - type: dropdown 48 | id: operating-system 49 | attributes: 50 | label: What OS are you seeing the problem on? 51 | multiple: true 52 | options: 53 | - Windows 54 | - Linux 55 | - MacOS 56 | - Other (please let us know if the "What happened" box) 57 | validations: 58 | required: true 59 | - type: dropdown 60 | id: compiler 61 | attributes: 62 | label: What compiler are you seeing the problem on? 63 | multiple: true 64 | options: 65 | - GCC 66 | - Clang 67 | - MSVC 68 | - Other (please let us know if the "What happened" box) 69 | validations: 70 | required: true 71 | - type: textarea 72 | id: logs 73 | attributes: 74 | label: Relevant log output 75 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 76 | render: shell 77 | - type: checkboxes 78 | id: terms 79 | attributes: 80 | label: Code of Conduct 81 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com) 82 | options: 83 | - label: I agree to follow this project's Code of Conduct 84 | required: true 85 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 🧪 2 | description: Have a great idea? Find something is missing? 3 | labels: ["enhancement"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | We'd love to hear your idea(s)! 9 | - type: input 10 | id: question 11 | attributes: 12 | label: "What would you like to see added?" 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: context 17 | attributes: 18 | label: Additional Context 19 | validations: 20 | required: true 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support-question.yml: -------------------------------------------------------------------------------- 1 | name: Support Question 🤹 2 | description: Have some questions? We can offer help. 3 | labels: ["question"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Don't hesitate to ask any question you might have! 9 | - type: input 10 | id: question 11 | attributes: 12 | label: "What's your question?" 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: context 17 | attributes: 18 | label: Additional Context 19 | validations: 20 | required: true 21 | -------------------------------------------------------------------------------- /.github/actions/badge/action.yml: -------------------------------------------------------------------------------- 1 | name: Regular badging sequence 2 | description: Publishes a badge based on the job status 3 | inputs: 4 | category: 5 | description: The subfolder where to group the badges 6 | required: true 7 | label: 8 | description: The label to you in the badge (this should be unique for each badge in a category) 9 | required: true 10 | github_token: 11 | description: The token to use to publish the changes 12 | required: false 13 | default: ${{ github.token }} 14 | runs: 15 | using: composite 16 | steps: 17 | - if: job.status == 'success' 18 | uses: ./.github/actions/badge/write 19 | with: 20 | category: ${{ inputs.category }} 21 | label: ${{ inputs.label }} 22 | - if: job.status == 'failure' 23 | uses: ./.github/actions/badge/write 24 | with: 25 | category: ${{ inputs.category }} 26 | label: ${{ inputs.label }} 27 | message: Failing 28 | color: red 29 | - uses: ./.github/actions/badge/publish 30 | with: 31 | github_token: ${{ inputs.github_token }} 32 | -------------------------------------------------------------------------------- /.github/actions/badge/publish/action.yml: -------------------------------------------------------------------------------- 1 | name: Publish a Badges 2 | description: Publishes all badges 3 | inputs: 4 | github_token: 5 | description: The token to use to publish the changes 6 | required: false 7 | default: ${{ github.token }} 8 | runs: 9 | using: composite 10 | steps: 11 | - uses: peaceiris/actions-gh-pages@v3 12 | with: 13 | github_token: ${{ inputs.github_token }} 14 | publish_branch: badges 15 | publish_dir: ./badges 16 | keep_files: true 17 | user_name: "github-actions[bot]" 18 | user_email: "github-actions[bot]@users.noreply.github.com" 19 | -------------------------------------------------------------------------------- /.github/actions/badge/write/action.yml: -------------------------------------------------------------------------------- 1 | name: Make a Badge 2 | description: Creates a JSON file for use with Sheilds.io. The default is a "brightgreen" "Passing" badge 3 | inputs: 4 | category: 5 | description: The subfolder where to group the badges 6 | required: true 7 | label: 8 | description: The label to you in the badge (this should be unqie for each badge in a category) 9 | required: true 10 | message: 11 | description: The message you wish to have in the badge 12 | required: false 13 | default: "Passing" 14 | color: 15 | description: The color you wish the badge to be 16 | required: false 17 | default: "brightgreen" 18 | runs: 19 | using: composite 20 | steps: 21 | - run: | 22 | mkdir -p badges/${{ inputs.category }}/${{ inputs.label }} 23 | echo '{ "schemaVersion": 1, "label": "${{ inputs.label }}", "message": "${{ inputs.message }}", "color": "${{ inputs.color }}" }' > badges/${{ inputs.category }}/${{ inputs.label }}/shields.json 24 | shell: bash 25 | -------------------------------------------------------------------------------- /.github/actions/install/boost-json/action.yml: -------------------------------------------------------------------------------- 1 | name: Install Boost.JSON 2 | description: Install Boost.JSON for building test application 3 | inputs: 4 | version: 5 | description: The desired Boost.JSON version to install 6 | required: false 7 | default: "1.78.0" 8 | runs: 9 | using: composite 10 | steps: 11 | - run: | 12 | cd /tmp 13 | wget https://github.com/boostorg/json/archive/boost-${{ inputs.version }}.tar.gz 14 | tar -zxf /tmp/boost-${{ inputs.version }}.tar.gz 15 | cd json-boost-${{ inputs.version }} 16 | sudo cp -vR include/boost /usr/local/include 17 | shell: bash 18 | -------------------------------------------------------------------------------- /.github/actions/install/cmake/action.yml: -------------------------------------------------------------------------------- 1 | name: Install CMake 2 | description: Download, Build and Cache CMake 3 | inputs: 4 | version: 5 | description: The desired CMake version to install 6 | required: true 7 | url: 8 | description: "The corresponding URL to download the source code from" 9 | required: true 10 | runs: 11 | using: composite 12 | steps: 13 | - name: Cache CMake 14 | id: cache-cmake 15 | uses: actions/cache@v4 16 | with: 17 | path: cmake-${{ inputs.version }} 18 | key: ${{ runner.name }}-${{ runner.os }}-${{ runner.arch }}-${{ job.container.id }}-cmake-${{ inputs.version }} 19 | - name: Build cmake 20 | if: steps.cache-cmake.outputs.cache-hit != 'true' 21 | run: | 22 | wget ${{ inputs.url }} 23 | tar -zxf cmake-${{ inputs.version }}.tar.gz 24 | cd cmake-${{ inputs.version }} 25 | ./bootstrap 26 | make -j $(nproc) 27 | shell: bash 28 | - name: Install cmake 29 | run: | 30 | cd cmake-${{ inputs.version }} 31 | # Depending if we run in on a GitHub Actions or from within a Docker image we have different permissions 32 | if [[ $EUID > 0 ]]; then 33 | # If we are not root then we need to sudo 34 | sudo make install 35 | else 36 | # Default docker image does not have users setup so we are only root and can not sudo 37 | make install 38 | fi 39 | shell: bash 40 | -------------------------------------------------------------------------------- /.github/actions/install/danielaparker-jsoncons/action.yml: -------------------------------------------------------------------------------- 1 | name: Install jsoncons 2 | description: Install jsoncons for building test application 3 | inputs: 4 | version: 5 | description: The desired jsoncons version to install 6 | required: false 7 | default: "1.3.2" 8 | runs: 9 | using: composite 10 | steps: 11 | - run: | 12 | cd /tmp 13 | wget https://github.com/danielaparker/jsoncons/archive/v${{ inputs.version }}.tar.gz 14 | tar -zxf /tmp/v${{ inputs.version }}.tar.gz 15 | cd jsoncons-${{ inputs.version }} 16 | cmake . 17 | sudo cmake --install . 18 | shell: bash 19 | -------------------------------------------------------------------------------- /.github/actions/install/gtest/action.yml: -------------------------------------------------------------------------------- 1 | name: Install GTest 2 | description: Install and setup GTest for linking and building test application 3 | runs: 4 | using: composite 5 | steps: 6 | - run: sudo apt-get install libgtest-dev lcov 7 | shell: bash 8 | - run: (cd /usr/src/gtest && sudo `which cmake` .) 9 | shell: bash 10 | - run: sudo make -j $(nproc) -C /usr/src/gtest 11 | shell: bash 12 | - run: sudo ln -s /usr/src/gtest/libgtest.a /usr/lib/libgtest.a 13 | shell: bash 14 | - run: sudo ln -s /usr/src/gtest/libgtest_main.a /usr/lib/libgtest_main.a 15 | shell: bash 16 | -------------------------------------------------------------------------------- /.github/actions/install/kazuho-picojson/action.yml: -------------------------------------------------------------------------------- 1 | name: Install PicoJSON 2 | description: Install PicoJSON for building test application 3 | inputs: 4 | version: 5 | description: The desired PicoJSON version to install 6 | required: false 7 | default: "v1.3.0" 8 | runs: 9 | using: composite 10 | steps: 11 | - run: | 12 | cd /tmp 13 | wget https://github.com/kazuho/picojson/archive/${{ inputs.version }}.tar.gz 14 | tar -zxf /tmp/${{ inputs.version }}.tar.gz 15 | cd picojson-${{ inputs.version }} 16 | sudo cp -v picojson.h /usr/local/include/picojson 17 | shell: bash 18 | -------------------------------------------------------------------------------- /.github/actions/install/libressl/action.yml: -------------------------------------------------------------------------------- 1 | name: Install LibreSSL 2 | description: Install and setup LibreSSL for linking and building test application 3 | inputs: 4 | version: 5 | description: The desired LibreSSL version to install 6 | required: false 7 | default: "3.3.0" 8 | runs: 9 | using: composite 10 | steps: 11 | - run: | 12 | wget https://raw.githubusercontent.com/libressl-portable/portable/v${{ inputs.version }}/FindLibreSSL.cmake -P cmake/ 13 | cd /tmp 14 | wget https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${{ inputs.version }}.tar.gz 15 | tar -zvxf /tmp/libressl-${{ inputs.version }}.tar.gz 16 | cd libressl-${{ inputs.version }} 17 | ./configure 18 | sudo make -j $(nproc) install 19 | shell: bash 20 | -------------------------------------------------------------------------------- /.github/actions/install/nlohmann-json/action.yml: -------------------------------------------------------------------------------- 1 | name: Install nlohmann-json 2 | description: Install nlohmann-json for building test application 3 | inputs: 4 | version: 5 | description: The desired nlohmann-json version to install 6 | required: false 7 | default: "3.12.0" 8 | runs: 9 | using: composite 10 | steps: 11 | - run: | 12 | cd /tmp 13 | wget https://github.com/nlohmann/json/archive/v${{ inputs.version }}.tar.gz 14 | tar -zxf /tmp/v${{ inputs.version }}.tar.gz 15 | cd json-${{ inputs.version }} 16 | cmake . 17 | sudo cmake --install . 18 | shell: bash 19 | -------------------------------------------------------------------------------- /.github/actions/install/open-source-parsers-jsoncpp/action.yml: -------------------------------------------------------------------------------- 1 | name: Install open-source-parsers/jsoncpp 2 | description: Install open-source-parsers/jsoncpp for building test application 3 | inputs: 4 | version: 5 | description: The desired open-source-parsers/jsoncpp version to install 6 | required: false 7 | default: "1.9.6" 8 | runs: 9 | using: composite 10 | steps: 11 | - run: | 12 | cd /tmp 13 | wget https://github.com/open-source-parsers/jsoncpp/archive/${{ inputs.version }}.tar.gz 14 | tar -zxf /tmp/${{ inputs.version }}.tar.gz 15 | cd jsoncpp-${{ inputs.version }} 16 | # https://github.com/open-source-parsers/jsoncpp/blob/69098a18b9af0c47549d9a271c054d13ca92b006/include/PreventInSourceBuilds.cmake#L8 17 | mkdir build 18 | cd build 19 | cmake .. -DJSONCPP_WITH_TESTS=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_OBJECT_LIBS=OFF 20 | cmake --build . 21 | sudo cmake --install . 22 | shell: bash 23 | -------------------------------------------------------------------------------- /.github/actions/install/openssl/action.yml: -------------------------------------------------------------------------------- 1 | name: Install OpenSSL 2 | description: Install and setup OpenSSL for linking and building test application 3 | inputs: 4 | version: 5 | description: The desired OpenSSL version to install 6 | required: false 7 | default: "openssl-3.0.0" 8 | runs: 9 | using: composite 10 | steps: 11 | - run: | 12 | cd /tmp 13 | wget https://github.com/openssl/openssl/archive/refs/tags/${{ inputs.version }}.tar.gz 14 | tar -zxf /tmp/${{ inputs.version }}.tar.gz 15 | cd openssl-${{ inputs.version }} 16 | ./config --prefix=/tmp --libdir=lib 17 | make -j $(nproc) 18 | sudo make -j $(nproc) install_sw 19 | echo "OPENSSL_ROOT_DIR=/tmp" >> "$GITHUB_ENV" 20 | echo "OpenSSL_ROOT=/tmp" >> "$GITHUB_ENV" 21 | shell: bash 22 | -------------------------------------------------------------------------------- /.github/actions/install/wolfssl/action.yml: -------------------------------------------------------------------------------- 1 | name: Install wolfSSL 2 | description: Install and setup wolfSSL for linking and building test application 3 | inputs: 4 | version: 5 | description: The desired stable wolfSSL version to install 6 | required: false 7 | default: "v4.8.1-stable" 8 | runs: 9 | using: composite 10 | steps: 11 | - run: | 12 | cd /tmp 13 | wget -O wolfssl.tar.gz https://github.com/wolfSSL/wolfssl/archive/${{ inputs.version }}.tar.gz 14 | tar -zxf /tmp/wolfssl.tar.gz 15 | cd wolfssl-* 16 | autoreconf -fiv 17 | ./configure --enable-opensslall --enable-opensslextra --disable-examples --disable-crypttests --enable-harden --enable-all --enable-all-crypto 18 | make 19 | sudo make install 20 | shell: bash 21 | - run: sudo rm -rf /usr/include/openssl 22 | shell: bash 23 | -------------------------------------------------------------------------------- /.github/actions/process-linting-results/action.yml: -------------------------------------------------------------------------------- 1 | name: Process Linting Results 2 | description: Add a comment to a pull request with when `git diff` present and save the changes as an artifact so they can be applied manually 3 | inputs: 4 | linter_name: 5 | description: The name of the tool to credit in the comment 6 | required: true 7 | runs: 8 | using: "composite" 9 | steps: 10 | - run: git add --update 11 | shell: bash 12 | - id: stage 13 | #continue-on-error: true 14 | uses: Thalhammer/patch-generator-action@v3 15 | 16 | # Unfortunately the previous action reports a failure so nothing else can run 17 | # partially a limitation on composite actions since `continue-on-error` is not 18 | # yet supported 19 | - if: steps.stage.outputs.result == 'dirty' 20 | uses: actions-ecosystem/action-create-comment@v1 21 | with: 22 | github_token: ${{ github.token }} 23 | body: | 24 | Hello, @${{ github.actor }}! `${{ inputs.linter_name }}` had some concerns :scream: 25 | - run: exit $(git status -uno -s | wc -l) 26 | shell: bash 27 | -------------------------------------------------------------------------------- /.github/actions/render/defaults/action.yml: -------------------------------------------------------------------------------- 1 | name: "Render `defaults.h` Template" 2 | description: "Generate the `defaults.h` header file for a JSON library" 3 | inputs: 4 | traits_name: 5 | description: "Name of the traits structure to be used. Typically in the format `author_repository` or equivilant" 6 | required: true 7 | library_name: 8 | description: "Name of the JSON library." 9 | required: true 10 | library_url: 11 | description: "URL to the JSON library." 12 | required: true 13 | disable_default_traits: 14 | description: "Set the macro to disable the default traits" 15 | required: false 16 | default: "true" 17 | outputs: 18 | file_path: 19 | description: "Relative path which the 'defaults.h' was written to" 20 | value: ${{ steps.script.outputs.result }} 21 | runs: 22 | using: composite 23 | steps: 24 | - uses: actions/setup-node@v3 25 | with: 26 | node-version: 14 27 | - run: npm install mustache 28 | shell: bash 29 | - uses: actions/github-script@v6 30 | id: script 31 | env: 32 | TRAITS_NAME: ${{ inputs.traits_name }} 33 | LIBRARY_NAME: ${{ inputs.library_name }} 34 | LIBRARY_URL: ${{ inputs.library_url }} 35 | DISABLE_DEFAULT_TRAITS: ${{ inputs.disable_default_traits }} 36 | with: 37 | result-encoding: string 38 | script: | 39 | const mustache = require('mustache') 40 | const path = require('path') 41 | const fs = require('fs') 42 | 43 | const { TRAITS_NAME, LIBRARY_NAME, LIBRARY_URL, DISABLE_DEFAULT_TRAITS } = process.env 44 | console.log(`Rendering ${TRAITS_NAME}!`) 45 | 46 | const disableDefault = DISABLE_DEFAULT_TRAITS === 'true' 47 | 48 | const template = fs.readFileSync(path.join('include', 'jwt-cpp', 'traits', 'defaults.h.mustache'), 'utf8') 49 | const content = mustache.render(template, { 50 | traits_name: TRAITS_NAME, 51 | traits_name_upper: TRAITS_NAME.toUpperCase(), 52 | library_name: LIBRARY_NAME, 53 | library_url: LIBRARY_URL, 54 | disable_default_traits: disableDefault, 55 | }) 56 | // https://dmitripavlutin.com/replace-all-string-occurrences-javascript/ 57 | function replaceAll(string, search, replace) { 58 | return string.split(search).join(replace); 59 | } 60 | 61 | const outputDir = path.join('include', 'jwt-cpp', 'traits', replaceAll(TRAITS_NAME, '_', '-')) 62 | fs.mkdirSync(outputDir, { recursive: true }) 63 | 64 | const filePath = path.join(outputDir, 'defaults.h') 65 | fs.writeFileSync(filePath, content) 66 | 67 | return filePath 68 | -------------------------------------------------------------------------------- /.github/actions/render/tests/action.yml: -------------------------------------------------------------------------------- 1 | name: "Render `TraitsTests.cpp` Template" 2 | description: "Generate the `TraitsTests.cpp` header file for a JSON library" 3 | inputs: 4 | traits_name: 5 | description: "Name of the traits structure to be used. Typically in the format `author_repository` or equivilant" 6 | required: true 7 | test_suite_name: 8 | description: "Name of the JSON library." 9 | required: true 10 | runs: 11 | using: composite 12 | steps: 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: 14 16 | - run: npm install mustache 17 | shell: bash 18 | - uses: actions/github-script@v6 19 | env: 20 | TRAITS_NAME: ${{ inputs.traits_name }} 21 | SUITE_NAME: ${{ inputs.test_suite_name }} 22 | with: 23 | script: | 24 | const mustache = require('mustache') 25 | const path = require('path') 26 | const fs = require('fs') 27 | 28 | const { TRAITS_NAME, SUITE_NAME } = process.env 29 | console.log(`Rendering ${TRAITS_NAME}!`) 30 | 31 | // https://dmitripavlutin.com/replace-all-string-occurrences-javascript/ 32 | function replaceAll(string, search, replace) { 33 | return string.split(search).join(replace); 34 | } 35 | 36 | const template = fs.readFileSync(path.join('tests', 'traits', 'TraitsTest.cpp.mustache'), 'utf8') 37 | const content = mustache.render(template, { 38 | traits_name: TRAITS_NAME, 39 | traits_dir: replaceAll(TRAITS_NAME, '_', '-'), 40 | test_suite_name: SUITE_NAME, 41 | }) 42 | const outputDir = path.join('tests', 'traits') 43 | fs.mkdirSync(outputDir, { recursive: true }) 44 | fs.writeFileSync(path.join(outputDir, `${SUITE_NAME}.cpp`), content) 45 | -------------------------------------------------------------------------------- /.github/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/security.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Issues 2 | 3 | If you believe you have found a security vulnerability in JWT-CPP, we encourage you to let us know right away. 4 | We will investigate all legitimate reports and do our best to quickly fix the problem. 5 | 6 | Please refer to the section below for our responsible disclosure policy: 7 | 8 | ## Disclosure Policy 9 | 10 | Please contact one or more of the maintainers using the email advertised on our GitHub profiles: 11 | 12 | - [@Thalhammer](https://github.com/Thalhammer) 13 | - [@prince-chrismc](https://github.com/prince-chrismc) 14 | 15 | Please provide as many details as possible about version, platform, and workflow as possible. 16 | Having steps and reproducible code is better and is always greatly appreciated. 17 | 18 | ## Supported Version 19 | 20 | Typically, fixes will be immediately released as a new patch release. However, older affected versions may receive 21 | a new patch upon request. 22 | -------------------------------------------------------------------------------- /.github/workflows/cross-platform.yml: -------------------------------------------------------------------------------- 1 | name: Cross-Platform CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | jobs: 9 | build: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | os: [macos-latest, windows-latest, ubuntu-latest] 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: configure 19 | run: cmake --preset examples 20 | 21 | - name: build 22 | run: cmake --build --preset examples 23 | 24 | - name: test 25 | run: | 26 | cmake --build --preset examples --target rsa-create-run 27 | cmake --build --preset examples --target rsa-verify-run 28 | 29 | - if: github.event_name == 'push' && always() 30 | uses: ./.github/actions/badge 31 | with: 32 | category: cross-platform 33 | label: ${{ matrix.os }} 34 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: ssciwr/doxygen-install@v1 13 | with: 14 | version: "1.10.0" 15 | - run: sudo apt install graphviz 16 | - run: | 17 | cmake . -DJWT_BUILD_DOCS=ON 18 | cmake --build . --target jwt-docs 19 | - if: github.event_name == 'push' 20 | name: deploy 21 | uses: peaceiris/actions-gh-pages@v3 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | publish_dir: ./build/html 25 | force_orphan: true 26 | -------------------------------------------------------------------------------- /.github/workflows/jwt.yml: -------------------------------------------------------------------------------- 1 | name: JWT CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | coverage: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: lukka/get-cmake@latest 15 | - uses: ./.github/actions/install/gtest 16 | - uses: ./.github/actions/install/danielaparker-jsoncons 17 | - uses: ./.github/actions/install/boost-json 18 | - uses: ./.github/actions/install/open-source-parsers-jsoncpp 19 | 20 | - name: configure 21 | run: cmake --preset coverage 22 | - name: run 23 | run: cmake --build --preset coverage 24 | 25 | - uses: coverallsapp/github-action@v2 26 | with: 27 | github-token: ${{ secrets.GITHUB_TOKEN }} 28 | file: build/coverage.info 29 | format: lcov 30 | 31 | fuzzing: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v4 35 | - uses: lukka/get-cmake@latest 36 | 37 | - name: configure 38 | run: cmake --preset ci-fuzzing 39 | - name: build 40 | run: cmake --build --preset ci-fuzzing 41 | 42 | - name: run 43 | run: | 44 | cmake --build --preset ci-fuzzing --target jwt-cpp-fuzz-BaseEncodeFuzz-run 45 | cmake --build --preset ci-fuzzing --target jwt-cpp-fuzz-BaseDecodeFuzz-run 46 | cmake --build --preset ci-fuzzing --target jwt-cpp-fuzz-TokenDecodeFuzz-run 47 | 48 | asan: 49 | runs-on: ubuntu-latest 50 | strategy: 51 | fail-fast: false 52 | matrix: 53 | openssl: 54 | - { tag: "openssl-3.0.5", name: "3.0.5" } 55 | - { tag: "OpenSSL_1_1_1q", name: "1.1.1q" } 56 | steps: 57 | - uses: actions/checkout@v4 58 | - uses: lukka/get-cmake@latest 59 | - uses: ./.github/actions/install/gtest 60 | - uses: ./.github/actions/install/openssl 61 | with: 62 | version: ${{ matrix.openssl.tag }} 63 | 64 | - name: configure 65 | run: cmake --preset ci-asan 66 | - name: build 67 | run: cmake --build --preset ci-asan 68 | 69 | - name: run 70 | run: | 71 | cmake --build --preset ci-asan --target private-claims-run 72 | cmake --build --preset ci-asan --target rsa-create-run 73 | cmake --build --preset ci-asan --target rsa-verify-run 74 | cmake --build --preset ci-asan --target jwks-verify-run 75 | cmake --build --preset ci-asan --target jwt-cpp-test-run 76 | 77 | ubsan: 78 | runs-on: ubuntu-latest 79 | steps: 80 | - uses: actions/checkout@v4 81 | - uses: lukka/get-cmake@latest 82 | - uses: ./.github/actions/install/gtest 83 | 84 | - name: configure 85 | run: cmake --preset ci-ubsan 86 | - name: build 87 | run: cmake --build --preset ci-ubsan -DCMAKE_CXX_STANDARD=20 88 | 89 | - name: run 90 | run: | 91 | cmake --build --preset ci-ubsan --target private-claims-run 92 | cmake --build --preset ci-ubsan --target rsa-create-run 93 | cmake --build --preset ci-ubsan --target rsa-verify-run 94 | cmake --build --preset ci-ubsan --target jwks-verify-run 95 | cmake --build --preset ci-ubsan --target jwt-cpp-test-run 96 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | clang-format: 11 | runs-on: ubuntu-22.04 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | files: 16 | - "include/jwt-cpp/*.h" 17 | - "include/jwt-cpp/traits/**/*.h" 18 | - "tests/*.cpp" 19 | - "tests/**/*.cpp" 20 | - "example/*.cpp" 21 | - "example/**/*.cpp" 22 | steps: 23 | - run: | 24 | sudo apt-get install clang-format-14 25 | shopt -s globstar 26 | - uses: actions/checkout@v4 27 | - run: clang-format-14 -i ${{ matrix.files }} 28 | - uses: ./.github/actions/process-linting-results 29 | with: 30 | linter_name: clang-format 31 | 32 | cmake-format: 33 | runs-on: ubuntu-latest 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | files: ["**/CMakeLists.txt", "cmake/code-coverage.cmake"] 38 | steps: 39 | - uses: actions/setup-python@v5 40 | with: 41 | python-version: "3.x" 42 | - run: pip install cmakelang 43 | - run: shopt -s globstar 44 | - uses: actions/checkout@v4 45 | - run: cmake-format -i ${{ matrix.files }} 46 | - uses: ./.github/actions/process-linting-results 47 | with: 48 | linter_name: cmake-format 49 | 50 | clang-tidy: 51 | runs-on: ubuntu-latest 52 | steps: 53 | - uses: lukka/get-cmake@latest 54 | - uses: actions/checkout@v4 55 | - name: configure 56 | run: cmake --preset examples -DCMAKE_CXX_CLANG_TIDY="clang-tidy;-fix" 57 | - name: run 58 | run: cmake --build --preset examples 59 | - uses: ./.github/actions/process-linting-results 60 | with: 61 | linter_name: clang-tidy 62 | 63 | render-defaults: 64 | runs-on: ubuntu-22.04 65 | strategy: 66 | fail-fast: false 67 | matrix: 68 | traits: 69 | - { name: "boost_json", library: "Boost.JSON", url: "https://github.com/boostorg/json", disable_pico: true } 70 | - { name: "danielaparker_jsoncons", library: "jsoncons", url: "https://github.com/danielaparker/jsoncons", disable_pico: true } 71 | - { name: "kazuho_picojson", library: "picojson", url: "https://github.com/kazuho/picojson", disable_pico: false } 72 | - { name: "nlohmann_json", library: "JSON for Modern C++", url: "https://github.com/nlohmann/json", disable_pico: true } 73 | - { name: "open_source_parsers_jsoncpp", library: "jsoncpp", url: "https://github.com/open-source-parsers/jsoncpp", disable_pico: true } 74 | name: render-defaults (${{ matrix.traits.name }}) 75 | steps: 76 | - uses: actions/checkout@v4 77 | - run: | 78 | sudo apt-get install clang-format-14 79 | - uses: ./.github/actions/render/defaults 80 | id: render 81 | with: 82 | traits_name: ${{ matrix.traits.name }} 83 | library_name: ${{ matrix.traits.library }} 84 | library_url: ${{ matrix.traits.url }} 85 | disable_default_traits: ${{ matrix.traits.disable_pico }} 86 | - run: clang-format-14 -i ${{ steps.render.outputs.file_path }} 87 | - run: git add ${{ steps.render.outputs.file_path }} 88 | - uses: ./.github/actions/process-linting-results 89 | with: 90 | linter_name: render-defaults 91 | 92 | render-tests: 93 | runs-on: ubuntu-22.04 94 | strategy: 95 | fail-fast: false 96 | matrix: 97 | traits: 98 | # - { name: "boost_json", suite: "BoostJsonTest" } # Currently needs work arounds for API limitations 99 | - { name: "danielaparker_jsoncons", suite: "JsonconsTest" } 100 | # - { name: "kazuho_picojson", suite: "PicoJsonTest" } # Currently the default everything tests against this! 101 | - { name: "nlohmann_json", suite: "NlohmannTest" } 102 | - { name: "open_source_parsers_jsoncpp", suite: "OspJsoncppTest" } 103 | name: render-tests (${{ matrix.traits.name }}) 104 | steps: 105 | - uses: actions/checkout@v4 106 | - run: | 107 | sudo apt-get install clang-format-14 108 | shopt -s globstar 109 | - uses: ./.github/actions/render/tests 110 | with: 111 | traits_name: ${{ matrix.traits.name }} 112 | test_suite_name: ${{ matrix.traits.suite }} 113 | - run: clang-format-14 -i tests/**/*.cpp 114 | - run: git add tests/traits/* 115 | - uses: ./.github/actions/process-linting-results 116 | with: 117 | linter_name: render-tests 118 | 119 | line-ending: 120 | runs-on: ubuntu-latest 121 | steps: 122 | - uses: actions/checkout@v4 123 | - run: git add --renormalize . 124 | - uses: ./.github/actions/process-linting-results 125 | with: 126 | linter_name: line-ending 127 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release CD 2 | 3 | on: 4 | # Allows you to run this workflow manually from the Actions tab 5 | workflow_dispatch: 6 | # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release 7 | release: 8 | types: [published] 9 | 10 | jobs: 11 | nuget: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Setup NuGet 16 | uses: NuGet/setup-nuget@v1 17 | with: 18 | nuget-api-key: ${{ secrets.nuget_api_key }} 19 | 20 | - name: Create NuGet pkg 21 | working-directory: ./nuget 22 | run: nuget pack jwt-cpp.nuspec 23 | 24 | - name: Publish NuGet pkg 25 | working-directory: ./nuget 26 | run: nuget push *.nupkg -Source 'https://api.nuget.org/v3/index.json' 27 | 28 | release-asset: 29 | if: github.event_name != 'workflow_dispatch' 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v4 33 | 34 | - run: tar --exclude='./.git' -vczf /tmp/jwt-cpp-${{ github.event.release.tag_name }}.tar.gz . 35 | - uses: shogo82148/actions-upload-release-asset@v1 36 | with: 37 | upload_url: ${{ github.event.release.upload_url }} 38 | asset_path: /tmp/jwt-cpp-${{ github.event.release.tag_name }}.tar.gz 39 | 40 | - run: zip -x './.git/*' -r /tmp/jwt-cpp-${{ github.event.release.tag_name }}.zip . 41 | - uses: shogo82148/actions-upload-release-asset@v1 42 | with: 43 | upload_url: ${{ github.event.release.upload_url }} 44 | asset_path: /tmp/jwt-cpp-${{ github.event.release.tag_name }}.zip 45 | -------------------------------------------------------------------------------- /.github/workflows/ssl.yml: -------------------------------------------------------------------------------- 1 | name: SSL Compatibility CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | openssl: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | openssl: 15 | - { tag: "openssl-3.0.5", name: "3.0.5" } 16 | - { tag: "OpenSSL_1_1_1q", name: "1.1.1q" } 17 | - { tag: "OpenSSL_1_1_0i", name: "1.1.0i" } # Do not bump, there's a broken in the autoconfig script and it's not maintained 18 | - { tag: "OpenSSL_1_0_2u", name: "1.0.2u" } 19 | - { tag: "OpenSSL_1_0_1u", name: "1.0.1u" } 20 | name: OpenSSL ${{ matrix.openssl.name }} 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: lukka/get-cmake@latest 24 | - uses: ./.github/actions/install/gtest 25 | - uses: ./.github/actions/install/openssl 26 | with: 27 | version: ${{ matrix.openssl.tag }} 28 | 29 | - name: configure 30 | run: cmake --preset unit-tests -DOPENSSL_ROOT_DIR=/tmp 31 | - run: cmake --build --preset unit-tests 32 | - name: test 33 | run: ctest --preset unit-tests --output-on-failure 34 | 35 | - if: github.event_name == 'push' && always() 36 | uses: ./.github/actions/badge 37 | with: 38 | category: openssl 39 | label: ${{ matrix.openssl.name }} 40 | 41 | openssl-no-deprecated: 42 | runs-on: ubuntu-latest 43 | name: OpenSSL 3.0 No Deprecated 44 | steps: 45 | - uses: actions/checkout@v4 46 | - uses: lukka/get-cmake@latest 47 | - uses: ./.github/actions/install/gtest 48 | - uses: ./.github/actions/install/openssl 49 | with: 50 | version: "openssl-3.0.5" 51 | 52 | - name: configure 53 | run: cmake --preset unit-tests -DOPENSSL_ROOT_DIR=/tmp -DCMAKE_CXX_FLAGS="-DOPENSSL_NO_DEPRECATED=1" -DCMAKE_C_FLAGS="-DOPENSSL_NO_DEPRECATED=1" 54 | - run: cmake --build --preset unit-tests 55 | - name: test 56 | run: ctest --preset unit-tests 57 | 58 | libressl: 59 | runs-on: ubuntu-latest 60 | strategy: 61 | fail-fast: false 62 | matrix: 63 | libressl: ["3.5.3", "3.4.3", "3.3.6"] 64 | name: LibreSSL ${{ matrix.libressl }} 65 | steps: 66 | - uses: actions/checkout@v4 67 | - uses: lukka/get-cmake@latest 68 | - uses: ./.github/actions/install/gtest 69 | - uses: ./.github/actions/install/libressl 70 | with: 71 | version: ${{ matrix.libressl }} 72 | 73 | - name: configure 74 | run: cmake --preset unit-tests -DJWT_SSL_LIBRARY:STRING=LibreSSL 75 | - run: cmake --build --preset unit-tests 76 | - name: test 77 | run: ctest --preset unit-tests 78 | 79 | - if: github.event_name == 'push' && always() 80 | uses: ./.github/actions/badge 81 | with: 82 | category: libressl 83 | label: ${{ matrix.libressl }} 84 | 85 | wolfssl: 86 | runs-on: ubuntu-latest 87 | strategy: 88 | matrix: 89 | wolfssl: 90 | - { ref: "v5.1.1-stable", name: "5.1.1"} 91 | - { ref: "v5.2.0-stable", name: "5.2.0" } 92 | - { ref: "v5.3.0-stable", name: "5.3.0"} 93 | - { ref: "v5.7.0-stable", name: "5.7.0"} 94 | name: wolfSSL ${{ matrix.wolfssl.name }} 95 | steps: 96 | - uses: actions/checkout@v4 97 | - uses: lukka/get-cmake@latest 98 | - uses: ./.github/actions/install/gtest 99 | - uses: ./.github/actions/install/wolfssl 100 | with: 101 | version: ${{ matrix.wolfssl.ref }} 102 | 103 | - name: configure 104 | run: cmake --preset unit-tests -DJWT_SSL_LIBRARY:STRING=wolfSSL 105 | - run: cmake --build --preset unit-tests 106 | - name: test 107 | run: ctest --preset unit-tests 108 | 109 | - if: github.event_name == 'push' && always() 110 | uses: ./.github/actions/badge 111 | with: 112 | category: wolfssl 113 | label: ${{ matrix.wolfssl.name }} 114 | -------------------------------------------------------------------------------- /.github/workflows/targets.yml: -------------------------------------------------------------------------------- 1 | name: Specific Targets CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | paths: 9 | - "CMakeLists.txt" 10 | - "cmake/**" 11 | - "include/jwt-cpp/**" 12 | - "tests/cmake/**" 13 | - ".github/actions/**" 14 | - ".github/workflows/targets.yml" 15 | 16 | jobs: 17 | gcc-4-8: 18 | if: false # There's no existing image with node20 and gcc4.8 https://github.com/actions/checkout/issues/1809 19 | name: GCC 4.8 20 | runs-on: ubuntu-latest 21 | container: 22 | image: ubuntu:bionic-20230530 # 18.04 23 | env: 24 | CC: /usr/bin/gcc-4.8 25 | CXX: /usr/bin/g++-4.8 26 | steps: 27 | - run: | 28 | apt-get update 29 | apt-get install -y g++-4.8 wget make libssl-dev 30 | - uses: actions/checkout@v3 # Can not be upgrade as v4 needs NodeJS 20 doesn't exist next to gcc-4.8 31 | - uses: ./.github/actions/install/cmake 32 | with: 33 | version: "3.26.3" 34 | url: "https://cmake.org/files/v3.26/cmake-3.26.3.tar.gz" 35 | 36 | - name: setup 37 | run: | 38 | mkdir build 39 | cd build 40 | cmake .. 41 | cmake --build . 42 | cmake --install . 43 | - name: test 44 | working-directory: tests/cmake 45 | run: | 46 | CC=gcc-4.8 CXX=g++-4.8 cmake . -DTEST:STRING="defaults-enabled" 47 | cmake --build . 48 | 49 | gcc-12: 50 | name: GCC 12 51 | runs-on: ubuntu-latest 52 | container: 53 | image: ubuntu:jammy-20231004 # 22.04 54 | env: 55 | CC: /usr/bin/gcc-12 56 | CXX: /usr/bin/g++-12 57 | steps: 58 | - run: | 59 | apt-get update 60 | apt-get install -y g++-12 wget make libssl-dev 61 | - uses: actions/checkout@v4 62 | - uses: ./.github/actions/install/cmake 63 | with: 64 | version: "3.26.3" 65 | url: "https://cmake.org/files/v3.26/cmake-3.26.3.tar.gz" 66 | 67 | - name: setup 68 | run: | 69 | mkdir build 70 | cd build 71 | cmake .. 72 | cmake --build . 73 | cmake --install . 74 | 75 | - name: test 76 | working-directory: tests/cmake 77 | run: | 78 | CC=gcc-12 CXX=g++-12 cmake . -DCMAKE_CXX_STANDARD=20 -DTEST:STRING="defaults-enabled" 79 | cmake --build . 80 | -------------------------------------------------------------------------------- /.github/workflows/traits.yml: -------------------------------------------------------------------------------- 1 | name: Traits CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | traits: 11 | name: Traits (${{ matrix.target.name }}) 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | target: 16 | - { name: "danielaparker-jsoncons", tag: "1.3.2", version: "v1.3.2" } 17 | - { name: "boost-json", tag: "1.78.0", version: "v1.80.0" } 18 | - { name: "nlohmann-json", tag: "3.12.0", version: "v3.12.0" } 19 | - { name: "kazuho-picojson", tag: "111c9be5188f7350c2eac9ddaedd8cca3d7bf394", version: "111c9be" } 20 | - { name: "open-source-parsers-jsoncpp", tag: "1.9.6", version: "v1.9.6" } 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: lukka/get-cmake@latest 24 | - name: setup 25 | run: | 26 | mkdir build 27 | cd build 28 | cmake .. -DJWT_BUILD_EXAMPLES=OFF 29 | sudo cmake --install . 30 | 31 | # Install the JSON library 32 | - if: matrix.target.name == 'danielaparker-jsoncons' 33 | uses: ./.github/actions/install/danielaparker-jsoncons 34 | with: 35 | version: ${{matrix.target.tag}} 36 | 37 | - if: matrix.target.name == 'boost-json' 38 | uses: ./.github/actions/install/boost-json 39 | with: 40 | version: ${{matrix.target.tag}} 41 | 42 | - if: matrix.target.name == 'nlohmann-json' 43 | uses: ./.github/actions/install/nlohmann-json 44 | with: 45 | version: ${{matrix.target.tag}} 46 | 47 | - if: matrix.target.name == 'kazuho-picojson' 48 | run: rm -rf include/picojson 49 | - if: matrix.target.name == 'kazuho-picojson' 50 | uses: ./.github/actions/install/kazuho-picojson 51 | with: 52 | version: ${{matrix.target.tag}} 53 | 54 | - if: matrix.target.name == 'open-source-parsers-jsoncpp' 55 | uses: ./.github/actions/install/open-source-parsers-jsoncpp 56 | with: 57 | version: ${{matrix.target.tag}} 58 | 59 | - name: test 60 | working-directory: example/traits 61 | run: | 62 | cmake . -DCMAKE_FIND_DEBUG_MODE=1 63 | cmake --build . --target ${{ matrix.target.name }} 64 | ./${{ matrix.target.name }} 65 | 66 | - name: badge success 67 | if: github.event_name == 'push' && success() 68 | uses: ./.github/actions/badge/write 69 | with: 70 | category: traits 71 | label: ${{ matrix.target.name }} 72 | message: ${{ matrix.target.version }} 73 | color: lightblue # turquoise 74 | - name: badge failure 75 | if: github.event_name == 'push' && !success() 76 | uses: ./.github/actions/badge/write 77 | with: 78 | category: traits 79 | label: ${{ matrix.target.name }} 80 | message: ${{ matrix.target.version }} 81 | color: orange 82 | - if: github.event_name == 'push' && always() 83 | uses: ./.github/actions/badge/publish 84 | with: 85 | github_token: ${{ secrets.GITHUB_TOKEN }} 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | # ========================= 255 | # Operating System Files 256 | # ========================= 257 | 258 | # OSX 259 | # ========================= 260 | 261 | .DS_Store 262 | .AppleDouble 263 | .LSOverride 264 | 265 | # Thumbnails 266 | ._* 267 | 268 | # Files that might appear in the root of a volume 269 | .DocumentRevisions-V100 270 | .fseventsd 271 | .Spotlight-V100 272 | .TemporaryItems 273 | .Trashes 274 | .VolumeIcon.icns 275 | 276 | # Directories potentially created on remote AFP share 277 | .AppleDB 278 | .AppleDesktop 279 | Network Trash Folder 280 | Temporary Items 281 | .apdisk 282 | 283 | # Windows 284 | # ========================= 285 | 286 | # Windows image file caches 287 | Thumbs.db 288 | ehthumbs.db 289 | 290 | # Folder config file 291 | Desktop.ini 292 | 293 | # Recycle Bin used on file shares 294 | $RECYCLE.BIN/ 295 | 296 | # Windows Installer files 297 | *.cab 298 | *.msi 299 | *.msm 300 | *.msp 301 | 302 | # Windows shortcuts 303 | *.lnk 304 | 305 | # Linux files 306 | test 307 | *.o 308 | *.o.d 309 | 310 | # IDE-specific files 311 | .vscode/ 312 | .vscode/!extensions.json # Allow to provide recommended extensions 313 | 314 | # ClangD cache files 315 | .cache 316 | 317 | build/* 318 | package-lock.json 319 | 320 | CMakeUserPresets.json 321 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-vscode.cpptools", 4 | "ms-vscode.cpptools-extension-pack", 5 | "ms-vscode.cmake-tools", 6 | "twxs.cmake", 7 | "matepek.vscode-catch2-test-adapter", 8 | "GitHub.vscode-github-actions" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | cmake_policy(VERSION 3.14) 3 | if(POLICY CMP0135) # DOWNLOAD_EXTRACT_TIMESTAMP 4 | cmake_policy(SET CMP0135 NEW) 5 | endif() 6 | 7 | # HUNTER_ENABLED is always set if this package is included in a project using hunter (HunterGate sets it) In this case 8 | # we will use hunter as well to stay consistent. If not the use can supply it on configure to force using hunter. 9 | if(HUNTER_ENABLED) 10 | include("cmake/HunterGate.cmake") 11 | huntergate(URL "https://github.com/cpp-pm/hunter/archive/v0.23.314.tar.gz" SHA1 12 | "95c47c92f68edb091b5d6d18924baabe02a6962a") 13 | message(STATUS "jwt-cpp: using hunter for dependency resolution") 14 | endif() 15 | 16 | project(jwt-cpp LANGUAGES CXX) 17 | 18 | option(JWT_BUILD_EXAMPLES "Configure CMake to build examples (or not)" ON) 19 | option(JWT_BUILD_TESTS "Configure CMake to build tests (or not)" OFF) 20 | option(JWT_BUILD_DOCS "Adds a target for building the doxygen documentation" OFF) 21 | option(JWT_ENABLE_COVERAGE "Enable code coverage testing" OFF) 22 | option(JWT_ENABLE_FUZZING "Enable fuzz testing" OFF) 23 | 24 | option(JWT_DISABLE_PICOJSON "Do not provide the picojson template specialiaze" OFF) 25 | option(JWT_DISABLE_BASE64 "Do not include the base64 implementation from this library" OFF) 26 | include(CMakeDependentOption) 27 | cmake_dependent_option(JWT_EXTERNAL_PICOJSON 28 | "Use find_package() to locate picojson, provided to integrate with package managers" OFF 29 | "NOT JWT_DISABLE_PICOJSON" OFF) 30 | cmake_dependent_option(JWT_EXTERNAL_NLOHMANN_JSON 31 | "Use find_package() to locate nlohman-json required for tests and examples" OFF 32 | "JWT_BUILD_EXAMPLES OR JWT_BUILD_TESTS" OFF) 33 | 34 | set(JWT_SSL_LIBRARY_OPTIONS OpenSSL LibreSSL wolfSSL) 35 | set(JWT_SSL_LIBRARY OpenSSL CACHE STRING "Determines which SSL library to build with") 36 | set_property(CACHE JWT_SSL_LIBRARY PROPERTY STRINGS ${JWT_SSL_LIBRARY_OPTIONS}) 37 | 38 | set(JWT_JSON_TRAITS_OPTIONS boost-json danielaparker-jsoncons kazuho-picojson nlohmann-json open-source-parsers-jsoncpp) 39 | 40 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") 41 | 42 | if(NOT JWT_SSL_LIBRARY IN_LIST JWT_SSL_LIBRARY_OPTIONS) 43 | message(FATAL_ERROR "JWT_SSL_LIBRARY must be one of ${JWT_SSL_LIBRARY_OPTIONS}") 44 | endif() 45 | 46 | # If Hunter is enabled, we configure it to resolve OpenSSL and warn the user if he selected an option not supported by 47 | # hunter. We fall back to the system library in this case. 48 | if(HUNTER_ENABLED) 49 | if(${JWT_SSL_LIBRARY} MATCHES "OpenSSL") 50 | hunter_add_package(OpenSSL) 51 | elseif(${JWT_SSL_LIBRARY} MATCHES "LibreSSL") 52 | message(WARNING "Hunter does not support LibreSSL yet, the system library will be used (if available)") 53 | elseif(${JWT_SSL_LIBRARY} MATCHES "wolfSSL") 54 | message(WARNING "Hunter does not support wolfSSL yet, the system library will be used (if available)") 55 | endif() 56 | if(JWT_EXTERNAL_PICOJSON) 57 | message(WARNING "Hunter does not support picojson yet, the system library will be used (if available)") 58 | endif() 59 | endif() 60 | 61 | # Lookup dependencies 62 | if(${JWT_SSL_LIBRARY} MATCHES "OpenSSL") 63 | find_package(OpenSSL 1.0.1 REQUIRED) 64 | elseif(${JWT_SSL_LIBRARY} MATCHES "LibreSSL") 65 | find_package(LibreSSL 3.0.0 REQUIRED) 66 | elseif(${JWT_SSL_LIBRARY} MATCHES "wolfSSL") 67 | find_package(PkgConfig REQUIRED) 68 | pkg_check_modules(wolfssl REQUIRED IMPORTED_TARGET wolfssl) 69 | list(TRANSFORM wolfssl_INCLUDE_DIRS APPEND "/wolfssl") # This is required to access OpenSSL compatibility API 70 | endif() 71 | 72 | if(NOT JWT_DISABLE_PICOJSON AND JWT_EXTERNAL_PICOJSON) 73 | find_package(picojson 1.3.0 REQUIRED) 74 | endif() 75 | 76 | if(JWT_BUILD_EXAMPLES OR JWT_BUILD_TESTS) 77 | if(JWT_EXTERNAL_NLOHMANN_JSON) 78 | message(STATUS "jwt-cpp: using find_package for nlohmann-json required for tests") 79 | find_package(nlohmann_json CONFIG REQUIRED) 80 | else() 81 | message(STATUS "jwt-cpp: using FetchContent for nlohmann-json required for tests") 82 | include(FetchContent) 83 | fetchcontent_declare(nlohmann_json 84 | URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz 85 | URL_MD5 e155202b2a589137f6804724bd182f12) 86 | fetchcontent_makeavailable(nlohmann_json) 87 | endif() 88 | endif() 89 | 90 | set(JWT_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include) 91 | set(JWT_HEADER_FILES ${JWT_INCLUDE_PATH}/jwt-cpp/jwt.h) 92 | foreach(traits ${JWT_JSON_TRAITS_OPTIONS}) 93 | list(APPEND JWT_HEADER_FILES ${JWT_INCLUDE_PATH}/jwt-cpp/traits/${traits}/defaults.h 94 | ${JWT_INCLUDE_PATH}/jwt-cpp/traits/${traits}/traits.h) 95 | endforeach() 96 | 97 | if(NOT JWT_DISABLE_BASE64) 98 | list(APPEND JWT_HEADER_FILES ${JWT_INCLUDE_PATH}/jwt-cpp/base.h) 99 | endif() 100 | 101 | add_library(jwt-cpp INTERFACE) 102 | add_library(jwt-cpp::jwt-cpp ALIAS jwt-cpp) # To match export 103 | target_compile_features(jwt-cpp INTERFACE cxx_std_11) 104 | if(JWT_DISABLE_BASE64) 105 | target_compile_definitions(jwt-cpp INTERFACE JWT_DISABLE_BASE64) 106 | endif() 107 | if(JWT_DISABLE_PICOJSON) 108 | target_compile_definitions(jwt-cpp INTERFACE JWT_DISABLE_PICOJSON) 109 | endif() 110 | 111 | include(GNUInstallDirs) 112 | include(CMakePackageConfigHelpers) 113 | target_include_directories(jwt-cpp INTERFACE $ 114 | $) 115 | 116 | if(${JWT_SSL_LIBRARY} MATCHES "OpenSSL") 117 | target_link_libraries(jwt-cpp INTERFACE OpenSSL::SSL OpenSSL::Crypto) 118 | endif() 119 | 120 | if(${JWT_SSL_LIBRARY} MATCHES "LibreSSL") 121 | target_link_libraries(jwt-cpp INTERFACE LibreSSL::TLS) 122 | endif() 123 | 124 | if(${JWT_SSL_LIBRARY} MATCHES "wolfSSL") 125 | target_link_libraries(jwt-cpp INTERFACE PkgConfig::wolfssl) 126 | # This is required to access OpenSSL compatibility API 127 | target_include_directories(jwt-cpp INTERFACE ${wolfssl_INCLUDE_DIRS}) 128 | # This flag is required to have the mandatory header included automatically 129 | # https://github.com/Thalhammer/jwt-cpp/pull/352#discussion_r1627971786 130 | # https://github.com/wolfSSL/wolfssl/blob/3b74a6402998a8b8839e25e31ba8ac74749aa9b0/wolfssl/wolfcrypt/settings.h#L58 131 | target_compile_definitions(jwt-cpp INTERFACE EXTERNAL_OPTS_OPENVPN) 132 | endif() 133 | 134 | if(NOT JWT_DISABLE_PICOJSON AND JWT_EXTERNAL_PICOJSON) 135 | target_link_libraries(jwt-cpp INTERFACE picojson::picojson>) 136 | endif() 137 | 138 | # Hunter needs relative paths so the files are placed correctly 139 | if(NOT JWT_CMAKE_FILES_INSTALL_DIR) 140 | set(JWT_CMAKE_FILES_INSTALL_DIR cmake) 141 | endif() 142 | 143 | configure_package_config_file( 144 | ${CMAKE_CURRENT_LIST_DIR}/cmake/jwt-cpp-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config.cmake 145 | INSTALL_DESTINATION ${JWT_CMAKE_FILES_INSTALL_DIR}) 146 | write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config-version.cmake VERSION 0.7.1 147 | COMPATIBILITY ExactVersion) 148 | 149 | install(TARGETS jwt-cpp EXPORT jwt-cpp-targets PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 150 | install(EXPORT jwt-cpp-targets NAMESPACE jwt-cpp:: FILE jwt-cpp-targets.cmake 151 | DESTINATION ${JWT_CMAKE_FILES_INSTALL_DIR}) 152 | install(DIRECTORY ${JWT_INCLUDE_PATH}/jwt-cpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 153 | if(NOT JWT_EXTERNAL_PICOJSON AND NOT JWT_DISABLE_PICOJSON) 154 | install(FILES ${JWT_INCLUDE_PATH}/picojson/picojson.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/picojson) 155 | endif() 156 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-config-version.cmake 157 | DESTINATION ${JWT_CMAKE_FILES_INSTALL_DIR}) 158 | 159 | if(JWT_BUILD_EXAMPLES) 160 | add_subdirectory(example) 161 | endif() 162 | 163 | if(JWT_BUILD_TESTS) 164 | enable_testing() 165 | add_subdirectory(tests) 166 | endif() 167 | 168 | if(JWT_ENABLE_FUZZING) 169 | add_subdirectory(tests/fuzz) 170 | endif() 171 | 172 | if(JWT_BUILD_DOCS) 173 | add_subdirectory(docs) 174 | endif() 175 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 25, 6 | "patch": 0 7 | }, 8 | "include": [ 9 | "example/CMakePresets.json", 10 | "tests/CMakePresets.json" 11 | ], 12 | "configurePresets": [ 13 | { 14 | "name": "dev", 15 | "displayName": "Development", 16 | "inherits": "debug", 17 | "environment": { 18 | "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" 19 | }, 20 | "cacheVariables": { 21 | "JWT_BUILD_EXAMPLES": "ON", 22 | "JWT_BUILD_TESTS": "ON" 23 | } 24 | } 25 | ], 26 | "buildPresets": [ 27 | { 28 | "name": "dev", 29 | "configurePreset": "dev", 30 | "configuration": "Debug" 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dominik Thalhammer 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 | -------------------------------------------------------------------------------- /cmake/CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 25, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "default", 11 | "displayName": "Default Config", 12 | "hidden": true, 13 | "binaryDir": "${sourceDir}/build", 14 | "cacheVariables": { 15 | "JWT_BUILD_EXAMPLES": "OFF", 16 | "JWT_BUILD_TESTS": "OFF" 17 | } 18 | }, 19 | { 20 | "name": "debug", 21 | "displayName": "Debug", 22 | "inherits": "default", 23 | "cacheVariables": { 24 | "CMAKE_BUILD_TYPE": "Debug" 25 | } 26 | }, 27 | { 28 | "name": "release", 29 | "displayName": "Release", 30 | "inherits": "default", 31 | "cacheVariables": { 32 | "CMAKE_BUILD_TYPE": "Release" 33 | } 34 | } 35 | ], 36 | "buildPresets": [ 37 | { 38 | "name": "debug", 39 | "configurePreset": "debug", 40 | "configuration": "Debug" 41 | }, 42 | { 43 | "name": "release", 44 | "configurePreset": "release", 45 | "configuration": "Release" 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /cmake/code-coverage.cmake: -------------------------------------------------------------------------------- 1 | set(COVERAGE_CMAKE "${CMAKE_BINARY_DIR}/cmake/CodeCoverage.cmake") 2 | if(NOT EXISTS ${COVERAGE_CMAKE}) 3 | set(COVERAGE_URL "https://raw.githubusercontent.com/bilke/cmake-modules/master/CodeCoverage.cmake") 4 | file(DOWNLOAD ${COVERAGE_URL} ${COVERAGE_CMAKE}) 5 | endif() 6 | 7 | include(${COVERAGE_CMAKE}) 8 | 9 | function(setup_coverage TARGET) 10 | target_compile_options(${TARGET} PRIVATE -g -O0 -fprofile-arcs -ftest-coverage) 11 | target_link_libraries(${TARGET} PRIVATE gcov) 12 | endfunction() 13 | -------------------------------------------------------------------------------- /cmake/jwt-cpp-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | set(JWT_DISABLE_PICOJSON @JWT_DISABLE_PICOJSON@) 4 | set(JWT_EXTERNAL_PICOJSON @JWT_EXTERNAL_PICOJSON@) 5 | set(JWT_SSL_LIBRARY @JWT_SSL_LIBRARY@) 6 | 7 | include(CMakeFindDependencyMacro) 8 | if(${JWT_SSL_LIBRARY} MATCHES "wolfSSL") 9 | find_dependency(PkgConfig REQUIRED) 10 | pkg_check_modules(wolfssl REQUIRED IMPORTED_TARGET wolfssl) 11 | list(TRANSFORM wolfssl_INCLUDE_DIRS APPEND "/wolfssl") # This is required to access OpenSSL compatibility API 12 | else() 13 | find_dependency(${JWT_SSL_LIBRARY} REQUIRED) 14 | endif() 15 | 16 | if(NOT JWT_DISABLE_PICOJSON AND JWT_EXTERNAL_PICOJSON) 17 | find_dependency(picojson REQUIRED) 18 | endif() 19 | 20 | include("${CMAKE_CURRENT_LIST_DIR}/jwt-cpp-targets.cmake") 21 | -------------------------------------------------------------------------------- /cmake/private-find-boost-json.cmake: -------------------------------------------------------------------------------- 1 | if(TARGET boost_json) 2 | return() 3 | endif() 4 | 5 | unset(BOOSTJSON_INCLUDE_DIR CACHE) 6 | find_path(BOOSTJSON_INCLUDE_DIR "boost/json.hpp" "boost/json/src.hpp") 7 | if(EXISTS "${BOOSTJSON_INCLUDE_DIR}/boost/json.hpp") 8 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/private-boost-json.cpp.in" "#include ") 9 | configure_file("${CMAKE_CURRENT_BINARY_DIR}/private-boost-json.cpp.in" private-boost-json.cpp COPYONLY) 10 | add_library(boost_json "${BOOSTJSON_INCLUDE_DIR}/boost/json.hpp" 11 | "${BOOSTJSON_INCLUDE_DIR}/boost/json/src.hpp" 12 | "${CMAKE_CURRENT_BINARY_DIR}/private-boost-json.cpp") 13 | target_include_directories(boost_json PUBLIC ${BOOSTJSON_INCLUDE_DIR}) 14 | target_compile_definitions(boost_json PUBLIC BOOST_JSON_STANDALONE) 15 | target_compile_features(boost_json PUBLIC cxx_std_17) 16 | endif() 17 | -------------------------------------------------------------------------------- /cmake/private-find-kazuho-picojson.cmake: -------------------------------------------------------------------------------- 1 | if(TARGET kazuho_picojson) 2 | return() 3 | endif() 4 | 5 | unset(PICOJSON_INCLUDE_DIR CACHE) 6 | find_path(PICOJSON_INCLUDE_DIR "picojson/picojson.h") 7 | if(EXISTS "${PICOJSON_INCLUDE_DIR}/picojson/picojson.h") 8 | add_library(kazuho_picojson INTERFACE) 9 | target_include_directories(kazuho_picojson INTERFACE ${PICOJSON_INCLUDE_DIR}) 10 | endif() 11 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | doxygen-awesome*.css 2 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(DOWNLOAD https://raw.githubusercontent.com/jothepro/doxygen-awesome-css/v2.2.1/doxygen-awesome.css 2 | ${CMAKE_CURRENT_LIST_DIR}/doxygen-awesome.css 3 | EXPECTED_HASH SHA256=9b5549928906e9974cc12dcdde9265e016dc2388ec72d5aa3209f4870914a0c8) 4 | file(DOWNLOAD https://raw.githubusercontent.com/jothepro/doxygen-awesome-css/v2.2.1/doxygen-awesome-sidebar-only.css 5 | ${CMAKE_CURRENT_LIST_DIR}/doxygen-awesome-sidebar-only.css 6 | EXPECTED_HASH SHA256=998328b27193b7be007a431bc9be1a6f6855ff4d8fa722ecfdfed79a8931409f) 7 | 8 | execute_process(COMMAND doxygen --version RESULT_VARIABLE DOXYGEN_VERSION_RESULT 9 | OUTPUT_VARIABLE DOXYGEN_VERSION_RAW_OUTPUT) 10 | if(NOT DOXYGEN_VERSION_RESULT EQUAL 0) 11 | message(AUTHOR_WARNING "Unable to get the version of doxygen") 12 | else() 13 | # Extracts the version from the output of the command run before 14 | string(REGEX MATCH "^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)" DOXYGEN_VERSION_OUTPUT 15 | "${DOXYGEN_VERSION_RAW_OUTPUT}") 16 | message(STATUS "doxygen version detected : ${DOXYGEN_VERSION_OUTPUT}") 17 | endif() 18 | 19 | add_custom_target(jwt-docs COMMAND doxygen ${CMAKE_CURRENT_LIST_DIR}/Doxyfile WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 20 | SOURCES ${CMAKE_CURRENT_LIST_DIR}/Doxyfile BYPRODUCTS ${CMAKE_BINARY_DIR}/html/index.html) 21 | add_custom_command( 22 | TARGET jwt-docs POST_BUILD COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan 23 | "You can prview the documentation: ${CMAKE_BINARY_DIR}/html/index.html") 24 | -------------------------------------------------------------------------------- /docs/faqs.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | ## Handling Tokens 4 | 5 | ### The generated JWT token can be decoded, is this correct and secure? 6 | 7 | This is the expected behavior. While the integrity of tokens is ensured by the generated/verified hash, 8 | the contents of the token are only **encoded and not encrypted**. This means you can be sure the token 9 | has not been modified by an unauthorized party, but you should not store confidential information in it. 10 | Anyone with access to the token can read all the claims you put into it. They can however not modify 11 | them unless they have the (private or symmetric) key used to generate the token. If you need to put 12 | confidential information into it, current industry recommends generating a random id and store the data on your 13 | server, using the id to look it up whenever you need. 14 | 15 | ### How can new keys be generated for my application? 16 | 17 | The algorithms provided are all based on OpenSSL, mixing other 18 | cryptographic tools might not work. 19 | 20 | Here are a few links for your convenience: 21 | 22 | - [RSA](https://stackoverflow.com/a/44474607) 23 | - [ED25519](https://stackoverflow.com/a/73118582) 24 | - [ES256](https://github.com/Thalhammer/jwt-cpp/blob/68309438cf30679d6581d6cfbfeea0c028d9ed04/example/es256k.cpp#L5) 25 | 26 | ### Can this library encrypt/decrypt claims? 27 | 28 | No it does not, see [#115](https://github.com/Thalhammer/jwt-cpp/issues/115) for more details. 29 | More importantly you probably don't want to be using JWTs for anything sensitive. Read [this](https://stackoverflow.com/a/43497242/8480874) 30 | for more. 31 | 32 | ### Why are my tokens immediately expired? 33 | 34 | If you are generating tokens that seem to immediately expire, you are likely mixing local time where it is not required. The JWT specification 35 | requires using UTC which this library does. 36 | 37 | Here is a simple example of creating a token that will expire in one hour: 38 | 39 | ```cpp 40 | auto token = jwt::create() 41 | .set_issued_now() 42 | .set_expires_in(std::chrono::seconds{3600}) 43 | .sign(jwt::algorithm::hs256{"secret"}); 44 | ``` 45 | 46 | ### Can you add claims to a signed token? 47 | 48 | The signature includes both the header and payload, according to the RFCs... changing the payload would cause a discrepancy. That should result in the token being rejected. For more details checkout [#194](https://github.com/Thalhammer/jwt-cpp/issues/194). 49 | 50 | ### Why does `jwt::basic_claim` have no `as_object()` method? 51 | 52 | This was brought up in [#212](https://github.com/Thalhammer/jwt-cpp/issues/212#issuecomment-1054344192) and 53 | [#101](https://github.com/Thalhammer/jwt-cpp/issues/101) as it's an excellent question. 54 | 55 | It simply was not required to handle the required keys in JWTs for signing or verification. All the the mandatory keys are numeric, string or array types which required type definitions and access. 56 | 57 | The alternative is to use the `to_json()` method and use the libraries own APIs to pick the data type you need. 58 | 59 | ## Build Issues 60 | 61 | ### Missing \_HMAC and \_EVP_sha256 symbols on Mac 62 | 63 | There seems to exists a problem with the included openssl library of MacOS. Make sure you link to one provided by brew. See [#6](https://github.com/Thalhammer/jwt-cpp/issues/6) for more details. 64 | 65 | ### Building on windows fails with syntax errors 66 | 67 | The header ``, which is often included in Windows projects, defines macros for MIN and MAX which conflicts 68 | with `std::numeric_limits`. See [#5](https://github.com/Thalhammer/jwt-cpp/issues/5) for more details or [this StackOverflow thread](https://stackoverflow.com/questions/13416418/define-nominmax-using-stdmin-max). 69 | 70 | To fix this do one of the following things: 71 | 72 | - define `NOMINMAX`, which suppresses this behavior 73 | - include this library before you `#include ` 74 | - place `#undef max` and `#undef min` before you include this library 75 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | There's a number of options to choice from. 4 | 5 | ## Overview 6 | 7 | It's strongly recommended to use a package manager, as JWT-CPP has dependencies for both cryptography and JSON libraries, having a tool to do the heavily lifting can be ideal. Examples of a C and C++ package manager are [Conan](https://conan.io/) and [vcpkg](https://vcpkg.io/). If the version is out of date please check with their respective communities before opening and issue here. 8 | 9 | When manually adding this dependency, and the dependencies this has, check the GitHub Actions and Workflows for some inspiration about how to go about it. 10 | 11 | ### Package Manager 12 | 13 | - Conan: 14 | - vcpkg: 15 | - Nuget: 16 | - Hunter: 17 | - Spack: 18 | - Xrepo: 19 | 20 | Looking for ways to contribute? Help by adding JWT-CPP to your favorite package manager! 21 | [Nixpkgs](https://github.com/NixOS/nixpkgs) for example. Currently many are behind the latest. 22 | 23 | ### Header Only 24 | 25 | Simply downloading the `include/` directory is possible. 26 | Make sure the `jwt-cpp/` subdirectories is visible during compilation. 27 | This **does require** correctly linking to OpenSSL or alternative cryptography library. 28 | 29 | The minimum is `jwt.h` but you will need to add the defines: 30 | 31 | - [`JWT_DISABLE_BASE64`](https://github.com/Thalhammer/jwt-cpp/blob/c9a511f436eaa13857336ebeb44dbc5b7860fe01/include/jwt-cpp/jwt.h#L11) 32 | - [`JWT_DISABLE_PICOJSON`](https://github.com/Thalhammer/jwt-cpp/blob/c9a511f436eaa13857336ebeb44dbc5b7860fe01/include/jwt-cpp/jwt.h#L4) 33 | 34 | In addition to providing your own JSON traits implementation, see [traits.md](traits.md) for more information. 35 | 36 | ### CMake 37 | 38 | Using `find_package` is recommended. Step you environment by [installing OpenSSL](https://github.com/openssl/openssl/blob/master/INSTALL.md). Once complete, configure and install the `jwt-cpp` target using CMake. 39 | 40 | A simple installation of JWT-CPP may look like 41 | 42 | ```sh 43 | cmake . 44 | cmake --build . # Make sure everything compiles and links together 45 | cmake --install . 46 | ``` 47 | 48 | Then from your own project 49 | 50 | ```cmake 51 | find_package(jwt-cpp CONFIG REQUIRED) 52 | 53 | target_link_libraries(my_app PRIVATE jwt-cpp::jwt-cpp) 54 | ``` 55 | 56 | #### Unsupported Alternatives 57 | 58 | There's also the possibility of using [`FetchContent`](https://cmake.org/cmake/help/latest/module/FetchContent.html#examples) in pull this this project to your build tree. 59 | 60 | ```cmake 61 | include(FetchContent) 62 | fetchcontent_declare(jwt-cpp 63 | GIT_REPOSITORY https://github.com/Thalhammer/jwt-cpp.git 64 | GIT_TAG 08bcf77a687fb06e34138e9e9fa12a4ecbe12332 # v0.7.0 release 65 | ) 66 | set(JWT_BUILD_EXAMPLES OFF CACHE BOOL "disable building examples" FORCE) 67 | fetchcontent_makeavailable(jwt-cpp) 68 | 69 | target_link_libraries(my_app PRIVATE jwt-cpp::jwt-cpp) 70 | ``` 71 | 72 | Lastly, you can use `add_subdirectory`, this is untested but should work. 73 | -------------------------------------------------------------------------------- /docs/overrides.css: -------------------------------------------------------------------------------- 1 | html { 2 | --top-nav-height: 150px 3 | } 4 | 5 | @media screen and (min-width: 768px) { 6 | #top { 7 | height: var(--top-nav-height); 8 | } 9 | 10 | #nav-tree, #side-nav { 11 | height: calc(100vh - var(--top-nav-height)) !important; 12 | } 13 | 14 | #side-nav { 15 | top: var(--top-nav-height); 16 | } 17 | } 18 | 19 | .paramname em { 20 | font-weight: 600; 21 | color: var(--primary-dark-color); 22 | } 23 | 24 | a code { 25 | color: var(--primary-color) !important; 26 | } 27 | -------------------------------------------------------------------------------- /docs/signing.md: -------------------------------------------------------------------------------- 1 | # Signing Tokens 2 | 3 | ## Custom Signature Algorithms 4 | 5 | The libraries design is open so you can implement your own algorithms, see [existing implementations](https://github.com/Thalhammer/jwt-cpp/blob/73f23419235661e89a304ba5ab09d6714fb8dd94/include/jwt-cpp/jwt.h#L874) for ideas. 6 | 7 | ```cpp 8 | struct your_algorithm{ 9 | std::string sign(const std::string& /*unused*/, std::error_code& ec) const { 10 | ec.clear(); 11 | // CALL YOUR METHOD HERE 12 | return {}; 13 | } 14 | void verify(const std::string& /*unused*/, const std::string& signature, std::error_code& ec) const { 15 | ec.clear(); 16 | if (!signature.empty()) { ec = error::signature_verification_error::invalid_signature; } 17 | 18 | // CALL YOUR METHOD HERE 19 | } 20 | std::string name() const { return "your_algorithm"; } 21 | }; 22 | ``` 23 | 24 | Then everything else is the same, just pass in your implementation such as: 25 | 26 | ```cpp 27 | auto token = jwt::create() 28 | .set_id("custom-algo-example") 29 | .set_issued_now() 30 | .set_expires_in(std::chrono::seconds{36000}) 31 | .set_payload_claim("sample", jwt::claim(std::string{"test"})) 32 | .sign(your_algorithm{/* what ever you want */}); 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/ssl.md: -------------------------------------------------------------------------------- 1 | # Cryptography Libraries 2 | 3 | The underlying cryptography libraries describe [here](../README.md#ssl-compatibility) can be selected when configuring CMake by explicitly setting `JWT_SSL_LIBRARY` to one of three values. The default is to use OpenSSL. 4 | 5 | - OpenSSL 6 | - LibreSSL 7 | - wolfSSL 8 | 9 | Here's an example: 10 | 11 | ```sh 12 | cmake . -DJWT_SSL_LIBRARY:STRING=wolfSSL 13 | ``` 14 | 15 | ## Supported Versions 16 | 17 | These are the version which are currently being tested: 18 | 19 | | OpenSSL | LibreSSL | wolfSSL | 20 | | ----------------- | -------------- | -------------- | 21 | | ![1.0.2u][o1.0.2] | ![3.3.6][l3.3] | ![5.1.1][w5.1] | 22 | | ![1.1.0i][o1.1.0] | ![3.4.3][l3.4] | ![5.2.0][w5.2] | 23 | | ![1.1.1q][o1.1.1] | ![3.5.3][l3.5] | ![5.3.0][w5.3] | 24 | | ![3.0.5][o3.0] | | | 25 | 26 | > [!NOTE] 27 | > A complete list of versions tested in the past can be found [here](https://github.com/Thalhammer/jwt-cpp/tree/badges). 28 | 29 | [o1.0.2]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/openssl/1.0.2u/shields.json 30 | [o1.1.0]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/openssl/1.1.0i/shields.json 31 | [o1.1.1]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/openssl/1.1.1q/shields.json 32 | [o3.0]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/openssl/3.0.5/shields.json 33 | [l3.3]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/libressl/3.3.6/shields.json 34 | [l3.4]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/libressl/3.4.3/shields.json 35 | [l3.5]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/libressl/3.5.3/shields.json 36 | [w5.1]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/wolfssl/5.1.1/shields.json 37 | [w5.2]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/wolfssl/5.2.0/shields.json 38 | [w5.3]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/wolfssl/5.3.0/shields.json 39 | 40 | ## Notes 41 | 42 | JWT-CPP relies on the OpenSSL API, as a result both LibreSSL and wolfSSL need to include their respective compatibility layers. 43 | Most system already have OpenSSL so it's important to make sure when compiling your application it only includes one. Otherwise you may have missing symbols when linking. 44 | -------------------------------------------------------------------------------- /docs/traits.md: -------------------------------------------------------------------------------- 1 | # JSON Traits 2 | 3 | Traits define the compatibility mapping for JWT-CPP required functionality to the JSON implementation of choice. 4 | 5 | ## Selecting a JSON library 6 | 7 | For your convenience there are serval traits implementation which provide some popular JSON libraries. They are: 8 | 9 | [![picojson][picojson]](https://github.com/kazuho/picojson) 10 | [![nlohmann][nlohmann]](https://github.com/nlohmann/json) 11 | [![jsoncons][jsoncons]](https://github.com/danielaparker/jsoncons) 12 | [![boostjson][boostjson]](https://github.com/boostorg/json) 13 | [![jsoncpp][jsoncpp]](https://github.com/open-source-parsers/jsoncpp) 14 | 15 | [picojson]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/kazuho-picojson/shields.json 16 | [nlohmann]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/nlohmann-json/shields.json 17 | [jsoncons]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/danielaparker-jsoncons/shields.json 18 | [boostjson]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/boost-json/shields.json 19 | [jsoncpp]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/open-source-parsers-jsoncpp/shields.json 20 | 21 | In order to maintain compatibility, [picojson](https://github.com/kazuho/picojson) is still used to provide a specialized `jwt::claim` along with all helpers. Defining `JWT_DISABLE_PICOJSON` will remove this optional dependency. It's possible to directly include the traits defaults for the other JSON libraries. See the [traits examples](https://github.com/Thalhammer/jwt-cpp/tree/master/example/traits) for details. 22 | 23 | ```cpp 24 | //include "jwt-cpp/traits/author-library/traits.h" 25 | #include "jwt-cpp/traits/nlohmann-json/traits.h" 26 | // There is also a "defaults.h" if you's like to skip providing the 27 | // template specializations for the JSON traits 28 | 29 | int main() { 30 | // All the provided traits are in jwt::traits namespace 31 | using traits = jwt::traits::nlohmann_json; 32 | 33 | const auto time = jwt::date::clock::now(); 34 | const auto token = jwt::create() 35 | .set_type("JWT") 36 | .set_issuer("auth.mydomain.io") 37 | .set_audience("mydomain.io") 38 | .set_issued_at(time) 39 | .set_not_before(time) 40 | .set_expires_at(time + std::chrono::minutes{2} + std::chrono::seconds{15}) 41 | .sign(jwt::algorithm::none{}); 42 | const auto decoded = jwt::decode(token); 43 | 44 | jwt::verify() 45 | .allow_algorithm(jwt::algorithm::none{}) 46 | .with_issuer("auth.mydomain.io") 47 | .with_audience("mydomain.io") 48 | .verify(decoded); 49 | ``` 50 | 51 | ## Providing your own JSON Traits 52 | 53 | There are several key items that need to be provided to a `jwt::basic_claim` in order for it to be interoperable with you JSON library of choice. 54 | 55 | * type specifications 56 | * conversion from generic "value type" to a specific type 57 | * serialization and parsing 58 | 59 | If ever you are not sure, the traits are heavily checked against static asserts to make sure you provide everything that's required. 60 | 61 | > [!important] 62 | > Not all JSON libraries are a like, you may need to extend certain types such that it can be used. See this [provided implementation](https://github.com/Thalhammer/jwt-cpp/blob/e6b92cca0b7088027269c481fa244e5c39df88ff/include/jwt-cpp/traits/danielaparker-jsoncons/traits.h#L18). 63 | 64 | ```cpp 65 | struct my_favorite_json_library_traits { 66 | // Type Specifications 67 | using value_type = json; // The generic "value type" implementation, most libraries have one 68 | using object_type = json::object_t; // The "map type" string to value 69 | using array_type = json::array_t; // The "list type" array of values 70 | using string_type = std::string; // The "list of chars", must be a narrow char 71 | using number_type = double; // The "precision type" 72 | using integer_type = int64_t; // The "integral type" 73 | using boolean_type = bool; // The "boolean type" 74 | 75 | // Translation between the implementation notion of type, to the jwt::json::type equivalent 76 | static jwt::json::type get_type(const value_type &val) { 77 | using jwt::json::type; 78 | 79 | if (val.type() == json::value_t::object) 80 | return type::object; 81 | if (val.type() == json::value_t::array) 82 | return type::array; 83 | if (val.type() == json::value_t::string) 84 | return type::string; 85 | if (val.type() == json::value_t::number_float) 86 | return type::number; 87 | if (val.type() == json::value_t::number_integer) 88 | return type::integer; 89 | if (val.type() == json::value_t::boolean) 90 | return type::boolean; 91 | 92 | throw std::logic_error("invalid type"); 93 | } 94 | 95 | // Conversion from generic value to specific type 96 | static object_type as_object(const value_type &val); 97 | static array_type as_array(const value_type &val); 98 | static string_type as_string(const value_type &val); 99 | static number_type as_number(const value_type &val); 100 | static integer_type as_integer(const value_type &val); 101 | static boolean_type as_boolean(const value_type &val); 102 | 103 | // serialization and parsing 104 | static bool parse(value_type &val, string_type str); 105 | static string_type serialize(const value_type &val); // with no extra whitespace, padding or indentation 106 | }; 107 | ``` 108 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(jwt-cpp-examples) 3 | 4 | if(NOT TARGET jwt-cpp) 5 | find_package(jwt-cpp CONFIG REQUIRED) 6 | endif() 7 | 8 | add_subdirectory(traits) 9 | 10 | if(JWT_DISABLE_PICOJSON) 11 | message(FATAL_ERROR "examples require picojson to be available!") 12 | endif() 13 | 14 | add_executable(print-claims print-claims.cpp) 15 | target_link_libraries(print-claims jwt-cpp::jwt-cpp) 16 | add_custom_target(print-claims-run COMMAND print-claims) 17 | 18 | add_executable(private-claims private-claims.cpp) 19 | target_link_libraries(private-claims jwt-cpp::jwt-cpp) 20 | add_custom_target(private-claims-run COMMAND private-claims) 21 | 22 | add_executable(rsa-create rsa-create.cpp) 23 | target_link_libraries(rsa-create jwt-cpp::jwt-cpp) 24 | add_custom_target(rsa-create-run COMMAND rsa-create) 25 | 26 | add_executable(rsa-verify rsa-verify.cpp) 27 | target_link_libraries(rsa-verify jwt-cpp::jwt-cpp) 28 | add_custom_target(rsa-verify-run COMMAND rsa-verify) 29 | 30 | add_executable(jwks-verify jwks-verify.cpp) 31 | target_link_libraries(jwks-verify jwt-cpp::jwt-cpp) 32 | add_custom_target(jwks-verify-run COMMAND jwks-verify) 33 | 34 | add_executable(es256k es256k.cpp) 35 | target_link_libraries(es256k jwt-cpp::jwt-cpp) 36 | 37 | add_executable(partial-claim-verifier partial-claim-verifier.cpp) 38 | target_link_libraries(partial-claim-verifier jwt-cpp::jwt-cpp nlohmann_json::nlohmann_json) 39 | -------------------------------------------------------------------------------- /example/CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 25, 6 | "patch": 0 7 | }, 8 | "include": [ 9 | "../cmake/CMakePresets.json" 10 | ], 11 | "configurePresets": [ 12 | { 13 | "name": "examples", 14 | "displayName": "Build Examples", 15 | "inherits": "release", 16 | "cacheVariables": { 17 | "JWT_BUILD_EXAMPLES": "ON" 18 | } 19 | } 20 | ], 21 | "buildPresets": [ 22 | { 23 | "name": "examples", 24 | "configurePreset": "examples", 25 | "configuration": "Release", 26 | "targets": [ 27 | "rsa-create", 28 | "rsa-verify" 29 | ] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /example/es256k.cpp: -------------------------------------------------------------------------------- 1 | /// @file es256k.cpp 2 | #include 3 | #include 4 | 5 | int main() { 6 | // openssl ecparam -name secp256k1 -genkey -noout -out ec-secp256k1-priv-key.pem 7 | std::string es256k_priv_key = R"(-----BEGIN EC PRIVATE KEY----- 8 | MHQCAQEEIArnQWnspKtjiVuZuzuZ/l1Uqqq8gb2unLJ/6U/Saf4ioAcGBSuBBAAK 9 | oUQDQgAEfy03KCKUpIPMIJBtIG4xOwGm0Np/yHKaK9EDZi0mZ7VUeeNKq476CU5X 10 | 940yusahgneePQrDMF2nWFEtBCOiXQ== 11 | -----END EC PRIVATE KEY-----)"; 12 | // openssl ec -in ec-secp256k1-priv-key.pem -pubout > ec-secp256k1-pub-key.pem 13 | std::string es256k_pub_key = R"(-----BEGIN PUBLIC KEY----- 14 | MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEfy03KCKUpIPMIJBtIG4xOwGm0Np/yHKa 15 | K9EDZi0mZ7VUeeNKq476CU5X940yusahgneePQrDMF2nWFEtBCOiXQ== 16 | -----END PUBLIC KEY-----)"; 17 | 18 | auto token = jwt::create() 19 | .set_issuer("auth0") 20 | .set_type("JWT") 21 | .set_id("es256k-create-example") 22 | .set_issued_now() 23 | .set_expires_in(std::chrono::seconds{36000}) 24 | .set_payload_claim("sample", jwt::claim(std::string{"test"})) 25 | .sign(jwt::algorithm::es256k(es256k_pub_key, es256k_priv_key, "", "")); 26 | 27 | std::cout << "token:\n" << token << '\n'; 28 | 29 | auto verify = jwt::verify() 30 | .allow_algorithm(jwt::algorithm::es256k(es256k_pub_key, es256k_priv_key, "", "")) 31 | .with_issuer("auth0"); 32 | 33 | auto decoded = jwt::decode(token); 34 | 35 | verify.verify(decoded); 36 | 37 | for (auto& e : decoded.get_header_json()) 38 | std::cout << e.first << " = " << e.second << '\n'; 39 | for (auto& e : decoded.get_payload_json()) 40 | std::cout << e.first << " = " << e.second << '\n'; 41 | } 42 | -------------------------------------------------------------------------------- /example/partial-claim-verifier.cpp: -------------------------------------------------------------------------------- 1 | /// @file partial-claim-verifier.cpp 2 | #include "jwt-cpp/traits/nlohmann-json/defaults.h" 3 | 4 | #include 5 | 6 | int main() { 7 | std::string const rsa_priv_key = R"(-----BEGIN PRIVATE KEY----- 8 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4ZtdaIrd1BPIJ 9 | tfnF0TjIK5inQAXZ3XlCrUlJdP+XHwIRxdv1FsN12XyMYO/6ymLmo9ryoQeIrsXB 10 | XYqlET3zfAY+diwCb0HEsVvhisthwMU4gZQu6TYW2s9LnXZB5rVtcBK69hcSlA2k 11 | ZudMZWxZcj0L7KMfO2rIvaHw/qaVOE9j0T257Z8Kp2CLF9MUgX0ObhIsdumFRLaL 12 | DvDUmBPr2zuh/34j2XmWwn1yjN/WvGtdfhXW79Ki1S40HcWnygHgLV8sESFKUxxQ 13 | mKvPUTwDOIwLFL5WtE8Mz7N++kgmDcmWMCHc8kcOIu73Ta/3D4imW7VbKgHZo9+K 14 | 3ESFE3RjAgMBAAECggEBAJTEIyjMqUT24G2FKiS1TiHvShBkTlQdoR5xvpZMlYbN 15 | tVWxUmrAGqCQ/TIjYnfpnzCDMLhdwT48Ab6mQJw69MfiXwc1PvwX1e9hRscGul36 16 | ryGPKIVQEBsQG/zc4/L2tZe8ut+qeaK7XuYrPp8bk/X1e9qK5m7j+JpKosNSLgJj 17 | NIbYsBkG2Mlq671irKYj2hVZeaBQmWmZxK4fw0Istz2WfN5nUKUeJhTwpR+JLUg4 18 | ELYYoB7EO0Cej9UBG30hbgu4RyXA+VbptJ+H042K5QJROUbtnLWuuWosZ5ATldwO 19 | u03dIXL0SH0ao5NcWBzxU4F2sBXZRGP2x/jiSLHcqoECgYEA4qD7mXQpu1b8XO8U 20 | 6abpKloJCatSAHzjgdR2eRDRx5PMvloipfwqA77pnbjTUFajqWQgOXsDTCjcdQui 21 | wf5XAaWu+TeAVTytLQbSiTsBhrnoqVrr3RoyDQmdnwHT8aCMouOgcC5thP9vQ8Us 22 | rVdjvRRbnJpg3BeSNimH+u9AHgsCgYEA0EzcbOltCWPHRAY7B3Ge/AKBjBQr86Kv 23 | TdpTlxePBDVIlH+BM6oct2gaSZZoHbqPjbq5v7yf0fKVcXE4bSVgqfDJ/sZQu9Lp 24 | PTeV7wkk0OsAMKk7QukEpPno5q6tOTNnFecpUhVLLlqbfqkB2baYYwLJR3IRzboJ 25 | FQbLY93E8gkCgYB+zlC5VlQbbNqcLXJoImqItgQkkuW5PCgYdwcrSov2ve5r/Acz 26 | FNt1aRdSlx4176R3nXyibQA1Vw+ztiUFowiP9WLoM3PtPZwwe4bGHmwGNHPIfwVG 27 | m+exf9XgKKespYbLhc45tuC08DATnXoYK7O1EnUINSFJRS8cezSI5eHcbQKBgQDC 28 | PgqHXZ2aVftqCc1eAaxaIRQhRmY+CgUjumaczRFGwVFveP9I6Gdi+Kca3DE3F9Pq 29 | PKgejo0SwP5vDT+rOGHN14bmGJUMsX9i4MTmZUZ5s8s3lXh3ysfT+GAhTd6nKrIE 30 | kM3Nh6HWFhROptfc6BNusRh1kX/cspDplK5x8EpJ0QKBgQDWFg6S2je0KtbV5PYe 31 | RultUEe2C0jYMDQx+JYxbPmtcopvZQrFEur3WKVuLy5UAy7EBvwMnZwIG7OOohJb 32 | vkSpADK6VPn9lbqq7O8cTedEHttm6otmLt8ZyEl3hZMaL3hbuRj6ysjmoFKx6CrX 33 | rK0/Ikt5ybqUzKCMJZg2VKGTxg== 34 | -----END PRIVATE KEY-----)"; 35 | 36 | auto role_claim = nlohmann::json{{"my-service", {{"roles", {"foo", "bar", "baz"}}}}}; 37 | 38 | auto token = jwt::create() 39 | .set_issuer("auth0") 40 | .set_type("JWT") 41 | .set_id("rsa-create-example") 42 | .set_issued_now() 43 | .set_expires_in(std::chrono::seconds{36000}) 44 | .set_payload_claim("resource-access", role_claim) 45 | .sign(jwt::algorithm::rs256("", rsa_priv_key, "", "")); 46 | 47 | std::cout << "token: " << token << '\n'; 48 | 49 | std::string const rsa_pub_key = R"(-----BEGIN PUBLIC KEY----- 50 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGbXWiK3dQTyCbX5xdE4 51 | yCuYp0AF2d15Qq1JSXT/lx8CEcXb9RbDddl8jGDv+spi5qPa8qEHiK7FwV2KpRE9 52 | 83wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVs 53 | WXI9C+yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT 54 | 69s7of9+I9l5lsJ9cozf1rxrXX4V1u/SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8 55 | AziMCxS+VrRPDM+zfvpIJg3JljAh3PJHDiLu902v9w+Iplu1WyoB2aPfitxEhRN0 56 | YwIDAQAB 57 | -----END PUBLIC KEY-----)"; 58 | 59 | auto decoded = jwt::decode(token); 60 | 61 | for (const auto& e : decoded.get_payload_json()) 62 | std::cout << e.first << " = " << e.second << '\n'; 63 | 64 | std::cout << '\n'; 65 | 66 | auto role_verifier = [](const jwt::verify_context& ctx, std::error_code& ec) { 67 | using error = jwt::error::token_verification_error; 68 | 69 | auto c = ctx.get_claim(false, ec); 70 | if (ec) return; 71 | if (c.get_type() == jwt::json::type::object) { 72 | auto obj = c.to_json(); 73 | try { 74 | auto roles = obj["my-service"]["roles"].get(); 75 | if (roles.end() == std::find(roles.begin(), roles.end(), "foo")) ec = error::claim_value_missmatch; 76 | } catch (const std::exception& ex) { ec = error::claim_value_missmatch; } 77 | } else 78 | ec = error::claim_type_missmatch; 79 | }; 80 | 81 | /* [verifier check custom claim] */ 82 | auto verifier = jwt::verify() 83 | .allow_algorithm(jwt::algorithm::rs256(rsa_pub_key, "", "", "")) 84 | .with_issuer("auth0") 85 | // Check for "foo" in /my-service/role 86 | .with_claim("resource-access", role_verifier); 87 | /* [verifier check custom claim] */ 88 | 89 | try { 90 | verifier.verify(decoded); 91 | std::cout << "Success!" << '\n'; 92 | } catch (const std::exception& ex) { std::cout << "Error: " << ex.what() << '\n'; } 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /example/print-claims.cpp: -------------------------------------------------------------------------------- 1 | /// @file print-claims.cpp 2 | #include 3 | #include 4 | 5 | int main() { 6 | const std::string token = 7 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-" 8 | "1L-FsOjtR6uE-L4E9zJutMWKIe1v1M"; 9 | auto decoded = jwt::decode(token); 10 | 11 | for (auto& e : decoded.get_payload_json()) 12 | std::cout << e.first << " = " << e.second << '\n'; 13 | } 14 | -------------------------------------------------------------------------------- /example/private-claims.cpp: -------------------------------------------------------------------------------- 1 | /// @file private-claims.cpp 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | using sec = std::chrono::seconds; 8 | using min = std::chrono::minutes; 9 | 10 | int main() { 11 | jwt::claim from_raw_json; 12 | std::istringstream iss{R"##({"api":{"array":[1,2,3],"null":null}})##"}; 13 | iss >> from_raw_json; 14 | 15 | jwt::claim::set_t list{"once", "twice"}; 16 | std::vector big_numbers{727663072ULL, 770979831ULL, 427239169ULL, 525936436ULL}; 17 | 18 | const auto time = jwt::date::clock::now(); 19 | const auto token = jwt::create() 20 | .set_type("JWT") 21 | .set_issuer("auth.mydomain.io") 22 | .set_audience("mydomain.io") 23 | .set_issued_at(time) 24 | .set_not_before(time - sec{15}) 25 | .set_expires_at(time + sec{15} + min{2}) 26 | .set_payload_claim("boolean", picojson::value(true)) 27 | .set_payload_claim("integer", picojson::value(int64_t{12345})) 28 | .set_payload_claim("precision", picojson::value(12.345)) 29 | .set_payload_claim("strings", jwt::claim(list)) 30 | .set_payload_claim("array", jwt::claim(big_numbers.begin(), big_numbers.end())) 31 | .set_payload_claim("object", from_raw_json) 32 | .sign(jwt::algorithm::none{}); 33 | 34 | const auto decoded = jwt::decode(token); 35 | 36 | const auto api_array = decoded.get_payload_claim("object").to_json().get("api").get("array"); 37 | std::cout << "api array = " << api_array << '\n'; 38 | 39 | /* [verify exact claim] */ 40 | jwt::verify() 41 | .allow_algorithm(jwt::algorithm::none{}) 42 | .with_issuer("auth.mydomain.io") 43 | .with_audience("mydomain.io") 44 | .with_claim("object", from_raw_json) // Match the exact JSON content 45 | .verify(decoded); 46 | /* [verify exact claim] */ 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /example/rsa-create.cpp: -------------------------------------------------------------------------------- 1 | /// @file rsa-create.cpp 2 | #include 3 | #include 4 | 5 | int main() { 6 | std::string const rsa_priv_key = R"(-----BEGIN PRIVATE KEY----- 7 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4ZtdaIrd1BPIJ 8 | tfnF0TjIK5inQAXZ3XlCrUlJdP+XHwIRxdv1FsN12XyMYO/6ymLmo9ryoQeIrsXB 9 | XYqlET3zfAY+diwCb0HEsVvhisthwMU4gZQu6TYW2s9LnXZB5rVtcBK69hcSlA2k 10 | ZudMZWxZcj0L7KMfO2rIvaHw/qaVOE9j0T257Z8Kp2CLF9MUgX0ObhIsdumFRLaL 11 | DvDUmBPr2zuh/34j2XmWwn1yjN/WvGtdfhXW79Ki1S40HcWnygHgLV8sESFKUxxQ 12 | mKvPUTwDOIwLFL5WtE8Mz7N++kgmDcmWMCHc8kcOIu73Ta/3D4imW7VbKgHZo9+K 13 | 3ESFE3RjAgMBAAECggEBAJTEIyjMqUT24G2FKiS1TiHvShBkTlQdoR5xvpZMlYbN 14 | tVWxUmrAGqCQ/TIjYnfpnzCDMLhdwT48Ab6mQJw69MfiXwc1PvwX1e9hRscGul36 15 | ryGPKIVQEBsQG/zc4/L2tZe8ut+qeaK7XuYrPp8bk/X1e9qK5m7j+JpKosNSLgJj 16 | NIbYsBkG2Mlq671irKYj2hVZeaBQmWmZxK4fw0Istz2WfN5nUKUeJhTwpR+JLUg4 17 | ELYYoB7EO0Cej9UBG30hbgu4RyXA+VbptJ+H042K5QJROUbtnLWuuWosZ5ATldwO 18 | u03dIXL0SH0ao5NcWBzxU4F2sBXZRGP2x/jiSLHcqoECgYEA4qD7mXQpu1b8XO8U 19 | 6abpKloJCatSAHzjgdR2eRDRx5PMvloipfwqA77pnbjTUFajqWQgOXsDTCjcdQui 20 | wf5XAaWu+TeAVTytLQbSiTsBhrnoqVrr3RoyDQmdnwHT8aCMouOgcC5thP9vQ8Us 21 | rVdjvRRbnJpg3BeSNimH+u9AHgsCgYEA0EzcbOltCWPHRAY7B3Ge/AKBjBQr86Kv 22 | TdpTlxePBDVIlH+BM6oct2gaSZZoHbqPjbq5v7yf0fKVcXE4bSVgqfDJ/sZQu9Lp 23 | PTeV7wkk0OsAMKk7QukEpPno5q6tOTNnFecpUhVLLlqbfqkB2baYYwLJR3IRzboJ 24 | FQbLY93E8gkCgYB+zlC5VlQbbNqcLXJoImqItgQkkuW5PCgYdwcrSov2ve5r/Acz 25 | FNt1aRdSlx4176R3nXyibQA1Vw+ztiUFowiP9WLoM3PtPZwwe4bGHmwGNHPIfwVG 26 | m+exf9XgKKespYbLhc45tuC08DATnXoYK7O1EnUINSFJRS8cezSI5eHcbQKBgQDC 27 | PgqHXZ2aVftqCc1eAaxaIRQhRmY+CgUjumaczRFGwVFveP9I6Gdi+Kca3DE3F9Pq 28 | PKgejo0SwP5vDT+rOGHN14bmGJUMsX9i4MTmZUZ5s8s3lXh3ysfT+GAhTd6nKrIE 29 | kM3Nh6HWFhROptfc6BNusRh1kX/cspDplK5x8EpJ0QKBgQDWFg6S2je0KtbV5PYe 30 | RultUEe2C0jYMDQx+JYxbPmtcopvZQrFEur3WKVuLy5UAy7EBvwMnZwIG7OOohJb 31 | vkSpADK6VPn9lbqq7O8cTedEHttm6otmLt8ZyEl3hZMaL3hbuRj6ysjmoFKx6CrX 32 | rK0/Ikt5ybqUzKCMJZg2VKGTxg== 33 | -----END PRIVATE KEY-----)"; 34 | 35 | auto token = jwt::create() 36 | .set_issuer("auth0") 37 | .set_type("JWT") 38 | .set_id("rsa-create-example") 39 | .set_issued_now() 40 | .set_expires_in(std::chrono::seconds{36000}) 41 | .set_payload_claim("sample", jwt::claim(std::string{"test"})) 42 | .sign(jwt::algorithm::rs256("", rsa_priv_key, "", "")); 43 | 44 | std::cout << "token:\n" << token << '\n'; 45 | } 46 | -------------------------------------------------------------------------------- /example/rsa-verify.cpp: -------------------------------------------------------------------------------- 1 | /// \file rsa-verify.cpp 2 | #include 3 | #include 4 | 5 | int main() { 6 | const std::string rsa_pub_key = R"(-----BEGIN PUBLIC KEY----- 7 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGbXWiK3dQTyCbX5xdE4 8 | yCuYp0AF2d15Qq1JSXT/lx8CEcXb9RbDddl8jGDv+spi5qPa8qEHiK7FwV2KpRE9 9 | 83wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVs 10 | WXI9C+yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT 11 | 69s7of9+I9l5lsJ9cozf1rxrXX4V1u/SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8 12 | AziMCxS+VrRPDM+zfvpIJg3JljAh3PJHDiLu902v9w+Iplu1WyoB2aPfitxEhRN0 13 | YwIDAQAB 14 | -----END PUBLIC KEY-----)"; 15 | 16 | const std::string token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9." 17 | "VA2i1ui1cnoD6I3wnji1WAVCf29EekysvevGrT2GXqK1dDMc8" 18 | "HAZCTQxa1Q8NppnpYV-hlqxh-X3Bb0JOePTGzjynpNZoJh2aHZD-" 19 | "GKpZt7OO1Zp8AFWPZ3p8Cahq8536fD8RiBES9jRsvChZvOqA7gMcFc4" 20 | "YD0iZhNIcI7a654u5yPYyTlf5kjR97prCf_OXWRn-bYY74zna4p_bP9oWCL4BkaoRcMxi-" 21 | "IR7kmVcCnvbYqyIrKloXP2qPO442RBGqU7Ov9" 22 | "sGQxiVqtRHKXZR9RbfvjrErY1KGiCp9M5i2bsUHadZEY44FE2jiOmx-" 23 | "uc2z5c05CCXqVSpfCjWbh9gQ"; 24 | 25 | /* [allow rsa algorithm] */ 26 | auto verify = jwt::verify() 27 | // We only need an RSA public key to verify tokens 28 | .allow_algorithm(jwt::algorithm::rs256(rsa_pub_key, "", "", "")) 29 | // We expect token to come from a known authorization server 30 | .with_issuer("auth0"); 31 | /* [allow rsa algorithm] */ 32 | 33 | auto decoded = jwt::decode(token); 34 | 35 | verify.verify(decoded); 36 | 37 | for (auto& e : decoded.get_header_json()) 38 | std::cout << e.first << " = " << e.second << '\n'; 39 | for (auto& e : decoded.get_payload_json()) 40 | std::cout << e.first << " = " << e.second << '\n'; 41 | } 42 | -------------------------------------------------------------------------------- /example/traits/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(jwt-cpp-traits) 3 | 4 | if(NOT TARGET jwt-cpp) 5 | find_package(jwt-cpp CONFIG REQUIRED) 6 | endif() 7 | 8 | find_package(jsoncons CONFIG) 9 | if(TARGET jsoncons) 10 | add_executable(danielaparker-jsoncons danielaparker-jsoncons.cpp) 11 | target_link_libraries(danielaparker-jsoncons jwt-cpp::jwt-cpp jsoncons) 12 | endif() 13 | 14 | include("../../cmake/private-find-boost-json.cmake") 15 | if(TARGET boost_json) 16 | add_executable(boost-json boost-json.cpp) 17 | target_link_libraries(boost-json jwt-cpp::jwt-cpp boost_json) 18 | endif() 19 | 20 | find_package(nlohmann_json CONFIG) 21 | if(TARGET nlohmann_json::nlohmann_json) 22 | add_executable(nlohmann-json nlohmann-json.cpp) 23 | target_link_libraries(nlohmann-json nlohmann_json::nlohmann_json jwt-cpp::jwt-cpp) 24 | endif() 25 | 26 | include("../../cmake/private-find-kazuho-picojson.cmake") 27 | if(TARGET kazuho_picojson) 28 | add_executable(kazuho-picojson kazuho-picojson.cpp) 29 | target_link_libraries(kazuho-picojson jwt-cpp::jwt-cpp kazuho_picojson) 30 | endif() 31 | 32 | find_package(jsoncpp CONFIG) 33 | if(TARGET jsoncpp_static) 34 | add_executable(open-source-parsers-jsoncpp open-source-parsers-jsoncpp.cpp) 35 | target_link_libraries(open-source-parsers-jsoncpp jsoncpp_static jwt-cpp::jwt-cpp) 36 | endif() 37 | -------------------------------------------------------------------------------- /example/traits/README.md: -------------------------------------------------------------------------------- 1 | # Traits Examples 2 | 3 | These example require upstream CMake installation to work. There are exceptions: 4 | 5 | - For Boost.JSON headers must be located by custom CMake. 6 | - For PicoJSON headers must be located by custom CMake. 7 | -------------------------------------------------------------------------------- /example/traits/boost-json.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/boost-json/traits.h" 2 | 3 | // #include // You may require this if you are not building it elsewhere 4 | #include 5 | #include 6 | 7 | int main() { 8 | using sec = std::chrono::seconds; 9 | using min = std::chrono::minutes; 10 | using traits = jwt::traits::boost_json; 11 | using claim = jwt::basic_claim; 12 | 13 | traits::value_type raw_value; 14 | traits::parse(raw_value, R"##({"api":{"array":[1,2,3],"null":null}})##"); 15 | claim from_raw_json(raw_value); 16 | 17 | claim::set_t list{"once", "twice"}; 18 | std::vector big_numbers{727663072ULL, 770979831ULL, 427239169ULL, 525936436ULL}; 19 | 20 | const auto time = jwt::date::clock::now(); 21 | const auto token = jwt::create() 22 | .set_type("JWT") 23 | .set_issuer("auth.mydomain.io") 24 | .set_audience("mydomain.io") 25 | .set_issued_at(time) 26 | .set_not_before(time) 27 | .set_expires_at(time + min{2} + sec{15}) 28 | .set_payload_claim("boolean", true) 29 | .set_payload_claim("integer", 12345) 30 | .set_payload_claim("precision", 12.3456789) 31 | .set_payload_claim("strings", claim(list)) 32 | .set_payload_claim("array", claim{big_numbers.begin(), big_numbers.end()}) 33 | .set_payload_claim("object", from_raw_json) 34 | .sign(jwt::algorithm::none{}); 35 | const auto decoded = jwt::decode(token); 36 | 37 | for (auto& e : decoded.get_header_json()) 38 | std::cout << e.key() << " = " << e.value() << '\n'; 39 | 40 | const auto array = 41 | traits::as_array(decoded.get_payload_claim("object").to_json().as_object()["api"].as_object()["array"]); 42 | std::cout << "payload /object/api/array = " << array << '\n'; 43 | 44 | jwt::verify() 45 | .allow_algorithm(jwt::algorithm::none{}) 46 | .with_issuer("auth.mydomain.io") 47 | .with_audience("mydomain.io") 48 | .with_claim("object", from_raw_json) 49 | .verify(decoded); 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /example/traits/danielaparker-jsoncons.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/danielaparker-jsoncons/traits.h" 2 | 3 | #include 4 | #include 5 | 6 | int main() { 7 | using sec = std::chrono::seconds; 8 | using min = std::chrono::minutes; 9 | using traits = jwt::traits::danielaparker_jsoncons; 10 | using claim = jwt::basic_claim; 11 | 12 | claim from_raw_json; 13 | std::istringstream iss{R"##({"api":{"array":[1,2,3],"null":null}})##"}; 14 | iss >> from_raw_json; 15 | 16 | claim::set_t list{"once", "twice"}; 17 | std::vector big_numbers{727663072ULL, 770979831ULL, 427239169ULL, 525936436ULL}; 18 | 19 | const auto time = jwt::date::clock::now(); 20 | const auto token = jwt::create() 21 | .set_type("JWT") 22 | .set_issuer("auth.mydomain.io") 23 | .set_audience("mydomain.io") 24 | .set_issued_at(time) 25 | .set_not_before(time) 26 | .set_expires_at(time + min{2} + sec{15}) 27 | .set_payload_claim("boolean", true) 28 | .set_payload_claim("integer", 12345) 29 | .set_payload_claim("precision", 12.3456789) 30 | .set_payload_claim("strings", list) 31 | .set_payload_claim("array", claim{big_numbers.begin(), big_numbers.end()}) 32 | .set_payload_claim("object", from_raw_json) 33 | .sign(jwt::algorithm::none{}); 34 | const auto decoded = jwt::decode(token); 35 | 36 | const auto array = traits::as_array(decoded.get_payload_claim("object").to_json()["api"]["array"]); 37 | std::cout << "payload /object/api/array = " << jsoncons::pretty_print(traits::json(array)) << '\n'; 38 | 39 | jwt::verify() 40 | .allow_algorithm(jwt::algorithm::none{}) 41 | .with_issuer("auth.mydomain.io") 42 | .with_audience("mydomain.io") 43 | .with_claim("object", from_raw_json) 44 | .verify(decoded); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /example/traits/kazuho-picojson.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/kazuho-picojson/traits.h" 2 | 3 | #include 4 | #include 5 | 6 | int main() { 7 | using sec = std::chrono::seconds; 8 | using min = std::chrono::minutes; 9 | using traits = jwt::traits::kazuho_picojson; 10 | using claim = jwt::basic_claim; 11 | 12 | claim from_raw_json; 13 | std::istringstream iss{R"##({"api":{"array":[1,2,3],"null":null}})##"}; 14 | iss >> from_raw_json; 15 | 16 | claim::set_t list{"once", "twice"}; 17 | std::vector big_numbers{727663072ULL, 770979831ULL, 427239169ULL, 525936436ULL}; 18 | 19 | const auto time = jwt::date::clock::now(); 20 | const auto token = jwt::create() 21 | .set_type("JWT") 22 | .set_issuer("auth.mydomain.io") 23 | .set_audience("mydomain.io") 24 | .set_issued_at(time) 25 | .set_not_before(time) 26 | .set_expires_at(time + min{2} + sec{15}) 27 | .set_payload_claim("boolean", picojson::value(true)) 28 | .set_payload_claim("integer", picojson::value(int64_t{12345})) 29 | .set_payload_claim("precision", picojson::value(12.345)) 30 | .set_payload_claim("strings", claim(list)) 31 | .set_payload_claim("array", claim(big_numbers.begin(), big_numbers.end())) 32 | .set_payload_claim("object", from_raw_json) 33 | .sign(jwt::algorithm::none{}); 34 | const auto decoded = jwt::decode(token); 35 | 36 | const auto api_array = decoded.get_payload_claim("object").to_json().get("api").get("array"); 37 | std::cout << "api array = " << api_array << '\n'; 38 | 39 | jwt::verify() 40 | .allow_algorithm(jwt::algorithm::none{}) 41 | .with_issuer("auth.mydomain.io") 42 | .with_audience("mydomain.io") 43 | .with_claim("object", from_raw_json) 44 | .verify(decoded); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /example/traits/nlohmann-json.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/nlohmann-json/traits.h" 2 | 3 | #include 4 | #include 5 | 6 | int main() { 7 | using sec = std::chrono::seconds; 8 | using min = std::chrono::minutes; 9 | using traits = jwt::traits::nlohmann_json; 10 | using claim = jwt::basic_claim; 11 | 12 | claim from_raw_json; 13 | std::istringstream iss{R"##({"api":{"array":[1,2,3],"null":null}})##"}; 14 | iss >> from_raw_json; 15 | 16 | claim::set_t list{"once", "twice"}; 17 | std::vector big_numbers{727663072ULL, 770979831ULL, 427239169ULL, 525936436ULL}; 18 | 19 | const auto time = jwt::date::clock::now(); 20 | const auto token = jwt::create() 21 | .set_type("JWT") 22 | .set_issuer("auth.mydomain.io") 23 | .set_audience("mydomain.io") 24 | .set_issued_at(time) 25 | .set_not_before(time) 26 | .set_expires_at(time + min{2} + sec{15}) 27 | .set_payload_claim("boolean", true) 28 | .set_payload_claim("integer", 12345) 29 | .set_payload_claim("precision", 12.3456789) 30 | .set_payload_claim("strings", list) 31 | .set_payload_claim("array", {big_numbers.begin(), big_numbers.end()}) 32 | .set_payload_claim("object", from_raw_json) 33 | .sign(jwt::algorithm::none{}); 34 | const auto decoded = jwt::decode(token); 35 | 36 | const auto array = traits::as_array(decoded.get_payload_claim("object").to_json()["api"]["array"]); 37 | std::cout << "payload /object/api/array = " << array << '\n'; 38 | 39 | jwt::verify() 40 | .allow_algorithm(jwt::algorithm::none{}) 41 | .with_issuer("auth.mydomain.io") 42 | .with_audience("mydomain.io") 43 | .with_claim("object", from_raw_json) 44 | .verify(decoded); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /example/traits/open-source-parsers-jsoncpp.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h" 2 | 3 | #include 4 | #include 5 | 6 | int main() { 7 | using sec = std::chrono::seconds; 8 | using min = std::chrono::minutes; 9 | using traits = jwt::traits::open_source_parsers_jsoncpp; 10 | using claim = jwt::basic_claim; 11 | 12 | claim from_raw_json; 13 | std::istringstream iss{R"##({"api":{"array":[1,2,3],"null":null}})##"}; 14 | iss >> from_raw_json; 15 | 16 | claim::set_t list{"once", "twice"}; 17 | std::vector big_numbers{727663072ULL, 770979831ULL, 427239169ULL, 525936436ULL}; 18 | 19 | const auto time = jwt::date::clock::now(); 20 | const auto token = jwt::create() 21 | .set_type("JWT") 22 | .set_issuer("auth.mydomain.io") 23 | .set_audience("mydomain.io") 24 | .set_issued_at(time) 25 | .set_not_before(time) 26 | .set_expires_at(time + min{2} + sec{15}) 27 | .set_payload_claim("boolean", true) 28 | .set_payload_claim("integer", 12345) 29 | .set_payload_claim("precision", 12.3456789) 30 | .set_payload_claim("strings", claim(list)) 31 | .set_payload_claim("array", {big_numbers.begin(), big_numbers.end()}) 32 | .set_payload_claim("object", from_raw_json) 33 | .sign(jwt::algorithm::none{}); 34 | const auto decoded = jwt::decode(token); 35 | 36 | const auto array = traits::as_array(decoded.get_payload_claim("object").to_json()["api"]["array"]); 37 | std::cout << "payload /object/api/array = " << array << '\n'; 38 | 39 | jwt::verify() 40 | .allow_algorithm(jwt::algorithm::none{}) 41 | .with_issuer("auth.mydomain.io") 42 | .with_audience("mydomain.io") 43 | .with_claim("object", from_raw_json) 44 | .verify(decoded); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/boost-json/defaults.h: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_BOOST_JSON_DEFAULTS_H 2 | #define JWT_CPP_BOOST_JSON_DEFAULTS_H 3 | 4 | #ifndef JWT_DISABLE_PICOJSON 5 | #define JWT_DISABLE_PICOJSON 6 | #endif 7 | 8 | #include "traits.h" 9 | 10 | namespace jwt { 11 | /** 12 | * \brief a class to store a generic [Boost.JSON](https://github.com/boostorg/json) value as claim 13 | * 14 | * This type is the specialization of the \ref basic_claim class which 15 | * uses the standard template types. 16 | */ 17 | using claim = basic_claim; 18 | 19 | /** 20 | * Create a verifier using the default clock 21 | * \return verifier instance 22 | */ 23 | inline verifier verify() { 24 | return verify(default_clock{}); 25 | } 26 | 27 | /** 28 | * Create a builder using the default clock 29 | * \return builder instance to create a new token 30 | */ 31 | inline builder create() { 32 | return builder(default_clock{}); 33 | } 34 | 35 | #ifndef JWT_DISABLE_BASE64 36 | /** 37 | * Decode a token 38 | * \param token Token to decode 39 | * \return Decoded token 40 | * \throw std::invalid_argument Token is not in correct format 41 | * \throw std::runtime_error Base64 decoding failed or invalid json 42 | */ 43 | inline decoded_jwt decode(const std::string& token) { 44 | return decoded_jwt(token); 45 | } 46 | #endif 47 | 48 | /** 49 | * Decode a token 50 | * \tparam Decode is callable, taking a string_type and returns a string_type. 51 | * It should ensure the padding of the input and then base64url decode and 52 | * return the results. 53 | * \param token Token to decode 54 | * \param decode The token to parse 55 | * \return Decoded token 56 | * \throw std::invalid_argument Token is not in correct format 57 | * \throw std::runtime_error Base64 decoding failed or invalid json 58 | */ 59 | template 60 | decoded_jwt decode(const std::string& token, Decode decode) { 61 | return decoded_jwt(token, decode); 62 | } 63 | 64 | /** 65 | * Parse a jwk 66 | * \param token JWK Token to parse 67 | * \return Parsed JWK 68 | * \throw std::runtime_error Token is not in correct format 69 | */ 70 | inline jwk parse_jwk(const traits::boost_json::string_type& token) { 71 | return jwk(token); 72 | } 73 | 74 | /** 75 | * Parse a jwks 76 | * \param token JWKs Token to parse 77 | * \return Parsed JWKs 78 | * \throw std::runtime_error Token is not in correct format 79 | */ 80 | inline jwks parse_jwks(const traits::boost_json::string_type& token) { 81 | return jwks(token); 82 | } 83 | 84 | /** 85 | * This type is the specialization of the \ref verify_ops::verify_context class which 86 | * uses the standard template types. 87 | */ 88 | using verify_context = verify_ops::verify_context; 89 | } // namespace jwt 90 | 91 | #endif // JWT_CPP_BOOST_JSON_DEFAULTS_H 92 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/boost-json/traits.h: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_BOOSTJSON_TRAITS_H 2 | #define JWT_CPP_BOOSTJSON_TRAITS_H 3 | 4 | #define JWT_DISABLE_PICOJSON 5 | #include "jwt-cpp/jwt.h" 6 | 7 | #include 8 | // if not boost JSON standalone then error... 9 | 10 | namespace jwt { 11 | /** 12 | * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. 13 | */ 14 | namespace traits { 15 | namespace json = boost::json; 16 | /// basic_claim's JSON trait implementation for Boost.JSON 17 | struct boost_json { 18 | using value_type = json::value; 19 | using object_type = json::object; 20 | using array_type = json::array; 21 | using string_type = std::string; 22 | using number_type = double; 23 | using integer_type = std::int64_t; 24 | using boolean_type = bool; 25 | 26 | static jwt::json::type get_type(const value_type& val) { 27 | using jwt::json::type; 28 | 29 | if (val.kind() == json::kind::bool_) return type::boolean; 30 | if (val.kind() == json::kind::int64) return type::integer; 31 | if (val.kind() == json::kind::uint64) // boost internally tracks two types of integers 32 | return type::integer; 33 | if (val.kind() == json::kind::double_) return type::number; 34 | if (val.kind() == json::kind::string) return type::string; 35 | if (val.kind() == json::kind::array) return type::array; 36 | if (val.kind() == json::kind::object) return type::object; 37 | 38 | throw std::logic_error("invalid type"); 39 | } 40 | 41 | static object_type as_object(const value_type& val) { 42 | if (val.kind() != json::kind::object) throw std::bad_cast(); 43 | return val.get_object(); 44 | } 45 | 46 | static array_type as_array(const value_type& val) { 47 | if (val.kind() != json::kind::array) throw std::bad_cast(); 48 | return val.get_array(); 49 | } 50 | 51 | static string_type as_string(const value_type& val) { 52 | if (val.kind() != json::kind::string) throw std::bad_cast(); 53 | return string_type{val.get_string()}; 54 | } 55 | 56 | static integer_type as_integer(const value_type& val) { 57 | switch (val.kind()) { 58 | case json::kind::int64: return val.get_int64(); 59 | case json::kind::uint64: return static_cast(val.get_uint64()); 60 | default: throw std::bad_cast(); 61 | } 62 | } 63 | 64 | static boolean_type as_boolean(const value_type& val) { 65 | if (val.kind() != json::kind::bool_) throw std::bad_cast(); 66 | return val.get_bool(); 67 | } 68 | 69 | static number_type as_number(const value_type& val) { 70 | if (val.kind() != json::kind::double_) throw std::bad_cast(); 71 | return val.get_double(); 72 | } 73 | 74 | static bool parse(value_type& val, string_type str) { 75 | val = json::parse(str); 76 | return true; 77 | } 78 | 79 | static std::string serialize(const value_type& val) { return json::serialize(val); } 80 | }; 81 | } // namespace traits 82 | } // namespace jwt 83 | 84 | #endif // JWT_CPP_BOOSTJSON_TRAITS_H 85 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/danielaparker-jsoncons/defaults.h: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_DANIELAPARKER_JSONCONS_DEFAULTS_H 2 | #define JWT_CPP_DANIELAPARKER_JSONCONS_DEFAULTS_H 3 | 4 | #ifndef JWT_DISABLE_PICOJSON 5 | #define JWT_DISABLE_PICOJSON 6 | #endif 7 | 8 | #include "traits.h" 9 | 10 | namespace jwt { 11 | /** 12 | * \brief a class to store a generic [jsoncons](https://github.com/danielaparker/jsoncons) value as claim 13 | * 14 | * This type is the specialization of the \ref basic_claim class which 15 | * uses the standard template types. 16 | */ 17 | using claim = basic_claim; 18 | 19 | /** 20 | * Create a verifier using the default clock 21 | * \return verifier instance 22 | */ 23 | inline verifier verify() { 24 | return verify(default_clock{}); 25 | } 26 | 27 | /** 28 | * Create a builder using the default clock 29 | * \return builder instance to create a new token 30 | */ 31 | inline builder create() { 32 | return builder(default_clock{}); 33 | } 34 | 35 | #ifndef JWT_DISABLE_BASE64 36 | /** 37 | * Decode a token 38 | * \param token Token to decode 39 | * \return Decoded token 40 | * \throw std::invalid_argument Token is not in correct format 41 | * \throw std::runtime_error Base64 decoding failed or invalid json 42 | */ 43 | inline decoded_jwt decode(const std::string& token) { 44 | return decoded_jwt(token); 45 | } 46 | #endif 47 | 48 | /** 49 | * Decode a token 50 | * \tparam Decode is callable, taking a string_type and returns a string_type. 51 | * It should ensure the padding of the input and then base64url decode and 52 | * return the results. 53 | * \param token Token to decode 54 | * \param decode The token to parse 55 | * \return Decoded token 56 | * \throw std::invalid_argument Token is not in correct format 57 | * \throw std::runtime_error Base64 decoding failed or invalid json 58 | */ 59 | template 60 | decoded_jwt decode(const std::string& token, Decode decode) { 61 | return decoded_jwt(token, decode); 62 | } 63 | 64 | /** 65 | * Parse a jwk 66 | * \param token JWK Token to parse 67 | * \return Parsed JWK 68 | * \throw std::runtime_error Token is not in correct format 69 | */ 70 | inline jwk parse_jwk(const traits::danielaparker_jsoncons::string_type& token) { 71 | return jwk(token); 72 | } 73 | 74 | /** 75 | * Parse a jwks 76 | * \param token JWKs Token to parse 77 | * \return Parsed JWKs 78 | * \throw std::runtime_error Token is not in correct format 79 | */ 80 | inline jwks parse_jwks(const traits::danielaparker_jsoncons::string_type& token) { 81 | return jwks(token); 82 | } 83 | 84 | /** 85 | * This type is the specialization of the \ref verify_ops::verify_context class which 86 | * uses the standard template types. 87 | */ 88 | using verify_context = verify_ops::verify_context; 89 | } // namespace jwt 90 | 91 | #endif // JWT_CPP_DANIELAPARKER_JSONCONS_DEFAULTS_H 92 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/defaults.h.mustache: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_{{traits_name_upper}}_DEFAULTS_H 2 | #define JWT_CPP_{{traits_name_upper}}_DEFAULTS_H 3 | {{#disable_default_traits}} 4 | 5 | #ifndef JWT_DISABLE_PICOJSON 6 | #define JWT_DISABLE_PICOJSON 7 | #endif 8 | {{/disable_default_traits}} 9 | 10 | #include "traits.h" 11 | 12 | namespace jwt { 13 | /** 14 | * \brief a class to store a generic [{{library_name}}]({{{library_url}}}) value as claim 15 | * 16 | * This type is the specialization of the \ref basic_claim class which 17 | * uses the standard template types. 18 | */ 19 | using claim = basic_claim; 20 | 21 | /** 22 | * Create a verifier using the default clock 23 | * \return verifier instance 24 | */ 25 | inline verifier verify() { 26 | return verify(default_clock{}); 27 | } 28 | 29 | /** 30 | * Create a builder using the default clock 31 | * \return builder instance to create a new token 32 | */ 33 | inline builder create() { 34 | return builder(default_clock{}); 35 | } 36 | 37 | #ifndef JWT_DISABLE_BASE64 38 | /** 39 | * Decode a token 40 | * \param token Token to decode 41 | * \return Decoded token 42 | * \throw std::invalid_argument Token is not in correct format 43 | * \throw std::runtime_error Base64 decoding failed or invalid json 44 | */ 45 | inline decoded_jwt decode(const std::string& token) { 46 | return decoded_jwt(token); 47 | } 48 | #endif 49 | 50 | /** 51 | * Decode a token 52 | * \tparam Decode is callable, taking a string_type and returns a string_type. 53 | * It should ensure the padding of the input and then base64url decode and 54 | * return the results. 55 | * \param token Token to decode 56 | * \param decode The token to parse 57 | * \return Decoded token 58 | * \throw std::invalid_argument Token is not in correct format 59 | * \throw std::runtime_error Base64 decoding failed or invalid json 60 | */ 61 | template 62 | decoded_jwt decode(const std::string& token, Decode decode) { 63 | return decoded_jwt(token, decode); 64 | } 65 | 66 | /** 67 | * Parse a jwk 68 | * \param token JWK Token to parse 69 | * \return Parsed JWK 70 | * \throw std::runtime_error Token is not in correct format 71 | */ 72 | inline jwk parse_jwk(const traits::{{traits_name}}::string_type& token) { 73 | return jwk(token); 74 | } 75 | 76 | /** 77 | * Parse a jwks 78 | * \param token JWKs Token to parse 79 | * \return Parsed JWKs 80 | * \throw std::runtime_error Token is not in correct format 81 | */ 82 | inline jwks parse_jwks(const traits::{{traits_name}}::string_type& token) { 83 | return jwks(token); 84 | } 85 | 86 | /** 87 | * This type is the specialization of the \ref verify_ops::verify_context class which 88 | * uses the standard template types. 89 | */ 90 | using verify_context = verify_ops::verify_context; 91 | } // namespace jwt 92 | 93 | #endif // JWT_CPP_{{traits_name_upper}}_DEFAULTS_H 94 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/kazuho-picojson/defaults.h: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_KAZUHO_PICOJSON_DEFAULTS_H 2 | #define JWT_CPP_KAZUHO_PICOJSON_DEFAULTS_H 3 | 4 | #include "traits.h" 5 | 6 | namespace jwt { 7 | /** 8 | * \brief a class to store a generic [picojson](https://github.com/kazuho/picojson) value as claim 9 | * 10 | * This type is the specialization of the \ref basic_claim class which 11 | * uses the standard template types. 12 | */ 13 | using claim = basic_claim; 14 | 15 | /** 16 | * Create a verifier using the default clock 17 | * \return verifier instance 18 | */ 19 | inline verifier verify() { 20 | return verify(default_clock{}); 21 | } 22 | 23 | /** 24 | * Create a builder using the default clock 25 | * \return builder instance to create a new token 26 | */ 27 | inline builder create() { 28 | return builder(default_clock{}); 29 | } 30 | 31 | #ifndef JWT_DISABLE_BASE64 32 | /** 33 | * Decode a token 34 | * \param token Token to decode 35 | * \return Decoded token 36 | * \throw std::invalid_argument Token is not in correct format 37 | * \throw std::runtime_error Base64 decoding failed or invalid json 38 | */ 39 | inline decoded_jwt decode(const std::string& token) { 40 | return decoded_jwt(token); 41 | } 42 | #endif 43 | 44 | /** 45 | * Decode a token 46 | * \tparam Decode is callable, taking a string_type and returns a string_type. 47 | * It should ensure the padding of the input and then base64url decode and 48 | * return the results. 49 | * \param token Token to decode 50 | * \param decode The token to parse 51 | * \return Decoded token 52 | * \throw std::invalid_argument Token is not in correct format 53 | * \throw std::runtime_error Base64 decoding failed or invalid json 54 | */ 55 | template 56 | decoded_jwt decode(const std::string& token, Decode decode) { 57 | return decoded_jwt(token, decode); 58 | } 59 | 60 | /** 61 | * Parse a jwk 62 | * \param token JWK Token to parse 63 | * \return Parsed JWK 64 | * \throw std::runtime_error Token is not in correct format 65 | */ 66 | inline jwk parse_jwk(const traits::kazuho_picojson::string_type& token) { 67 | return jwk(token); 68 | } 69 | 70 | /** 71 | * Parse a jwks 72 | * \param token JWKs Token to parse 73 | * \return Parsed JWKs 74 | * \throw std::runtime_error Token is not in correct format 75 | */ 76 | inline jwks parse_jwks(const traits::kazuho_picojson::string_type& token) { 77 | return jwks(token); 78 | } 79 | 80 | /** 81 | * This type is the specialization of the \ref verify_ops::verify_context class which 82 | * uses the standard template types. 83 | */ 84 | using verify_context = verify_ops::verify_context; 85 | } // namespace jwt 86 | 87 | #endif // JWT_CPP_KAZUHO_PICOJSON_DEFAULTS_H 88 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/kazuho-picojson/traits.h: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_PICOJSON_TRAITS_H 2 | #define JWT_CPP_PICOJSON_TRAITS_H 3 | 4 | #ifndef PICOJSON_USE_INT64 5 | #define PICOJSON_USE_INT64 6 | #endif 7 | #include "picojson/picojson.h" 8 | 9 | #ifndef JWT_DISABLE_PICOJSON 10 | #define JWT_DISABLE_PICOJSON 11 | #endif 12 | #include "jwt-cpp/jwt.h" 13 | 14 | namespace jwt { 15 | /** 16 | * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. 17 | */ 18 | namespace traits { 19 | /// basic_claim's JSON trait implementation for picojson 20 | struct kazuho_picojson { 21 | using value_type = picojson::value; 22 | using object_type = picojson::object; 23 | using array_type = picojson::array; 24 | using string_type = std::string; 25 | using number_type = double; 26 | using integer_type = int64_t; 27 | using boolean_type = bool; 28 | 29 | static json::type get_type(const picojson::value& val) { 30 | using json::type; 31 | if (val.is()) return type::boolean; 32 | if (val.is()) return type::integer; 33 | if (val.is()) return type::number; 34 | if (val.is()) return type::string; 35 | if (val.is()) return type::array; 36 | if (val.is()) return type::object; 37 | 38 | throw std::logic_error("invalid type"); 39 | } 40 | 41 | static picojson::object as_object(const picojson::value& val) { 42 | if (!val.is()) throw std::bad_cast(); 43 | return val.get(); 44 | } 45 | 46 | static std::string as_string(const picojson::value& val) { 47 | if (!val.is()) throw std::bad_cast(); 48 | return val.get(); 49 | } 50 | 51 | static picojson::array as_array(const picojson::value& val) { 52 | if (!val.is()) throw std::bad_cast(); 53 | return val.get(); 54 | } 55 | 56 | static int64_t as_integer(const picojson::value& val) { 57 | if (!val.is()) throw std::bad_cast(); 58 | return val.get(); 59 | } 60 | 61 | static bool as_boolean(const picojson::value& val) { 62 | if (!val.is()) throw std::bad_cast(); 63 | return val.get(); 64 | } 65 | 66 | static double as_number(const picojson::value& val) { 67 | if (!val.is()) throw std::bad_cast(); 68 | return val.get(); 69 | } 70 | 71 | static bool parse(picojson::value& val, const std::string& str) { 72 | return picojson::parse(val, str).empty(); 73 | } 74 | 75 | static std::string serialize(const picojson::value& val) { return val.serialize(); } 76 | }; 77 | } // namespace traits 78 | } // namespace jwt 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/nlohmann-json/defaults.h: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_NLOHMANN_JSON_DEFAULTS_H 2 | #define JWT_CPP_NLOHMANN_JSON_DEFAULTS_H 3 | 4 | #ifndef JWT_DISABLE_PICOJSON 5 | #define JWT_DISABLE_PICOJSON 6 | #endif 7 | 8 | #include "traits.h" 9 | 10 | namespace jwt { 11 | /** 12 | * \brief a class to store a generic [JSON for Modern C++](https://github.com/nlohmann/json) value as claim 13 | * 14 | * This type is the specialization of the \ref basic_claim class which 15 | * uses the standard template types. 16 | */ 17 | using claim = basic_claim; 18 | 19 | /** 20 | * Create a verifier using the default clock 21 | * \return verifier instance 22 | */ 23 | inline verifier verify() { 24 | return verify(default_clock{}); 25 | } 26 | 27 | /** 28 | * Create a builder using the default clock 29 | * \return builder instance to create a new token 30 | */ 31 | inline builder create() { 32 | return builder(default_clock{}); 33 | } 34 | 35 | #ifndef JWT_DISABLE_BASE64 36 | /** 37 | * Decode a token 38 | * \param token Token to decode 39 | * \return Decoded token 40 | * \throw std::invalid_argument Token is not in correct format 41 | * \throw std::runtime_error Base64 decoding failed or invalid json 42 | */ 43 | inline decoded_jwt decode(const std::string& token) { 44 | return decoded_jwt(token); 45 | } 46 | #endif 47 | 48 | /** 49 | * Decode a token 50 | * \tparam Decode is callable, taking a string_type and returns a string_type. 51 | * It should ensure the padding of the input and then base64url decode and 52 | * return the results. 53 | * \param token Token to decode 54 | * \param decode The token to parse 55 | * \return Decoded token 56 | * \throw std::invalid_argument Token is not in correct format 57 | * \throw std::runtime_error Base64 decoding failed or invalid json 58 | */ 59 | template 60 | decoded_jwt decode(const std::string& token, Decode decode) { 61 | return decoded_jwt(token, decode); 62 | } 63 | 64 | /** 65 | * Parse a jwk 66 | * \param token JWK Token to parse 67 | * \return Parsed JWK 68 | * \throw std::runtime_error Token is not in correct format 69 | */ 70 | inline jwk parse_jwk(const traits::nlohmann_json::string_type& token) { 71 | return jwk(token); 72 | } 73 | 74 | /** 75 | * Parse a jwks 76 | * \param token JWKs Token to parse 77 | * \return Parsed JWKs 78 | * \throw std::runtime_error Token is not in correct format 79 | */ 80 | inline jwks parse_jwks(const traits::nlohmann_json::string_type& token) { 81 | return jwks(token); 82 | } 83 | 84 | /** 85 | * This type is the specialization of the \ref verify_ops::verify_context class which 86 | * uses the standard template types. 87 | */ 88 | using verify_context = verify_ops::verify_context; 89 | } // namespace jwt 90 | 91 | #endif // JWT_CPP_NLOHMANN_JSON_DEFAULTS_H 92 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/nlohmann-json/traits.h: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_NLOHMANN_JSON_TRAITS_H 2 | #define JWT_CPP_NLOHMANN_JSON_TRAITS_H 3 | 4 | #include "jwt-cpp/jwt.h" 5 | #include "nlohmann/json.hpp" 6 | 7 | namespace jwt { 8 | /** 9 | * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. 10 | */ 11 | namespace traits { 12 | /// basic_claim's JSON trait implementation for Modern C++ JSON 13 | struct nlohmann_json { 14 | using json = nlohmann::json; 15 | using value_type = json; 16 | using object_type = json::object_t; 17 | using array_type = json::array_t; 18 | using string_type = std::string; // current limitation of traits implementation 19 | using number_type = json::number_float_t; 20 | using integer_type = json::number_integer_t; 21 | using boolean_type = json::boolean_t; 22 | 23 | static jwt::json::type get_type(const json& val) { 24 | using jwt::json::type; 25 | 26 | if (val.type() == json::value_t::boolean) return type::boolean; 27 | // nlohmann internally tracks two types of integers 28 | if (val.type() == json::value_t::number_integer) return type::integer; 29 | if (val.type() == json::value_t::number_unsigned) return type::integer; 30 | if (val.type() == json::value_t::number_float) return type::number; 31 | if (val.type() == json::value_t::string) return type::string; 32 | if (val.type() == json::value_t::array) return type::array; 33 | if (val.type() == json::value_t::object) return type::object; 34 | 35 | throw std::logic_error("invalid type"); 36 | } 37 | 38 | static json::object_t as_object(const json& val) { 39 | if (val.type() != json::value_t::object) throw std::bad_cast(); 40 | return val.get(); 41 | } 42 | 43 | static std::string as_string(const json& val) { 44 | if (val.type() != json::value_t::string) throw std::bad_cast(); 45 | return val.get(); 46 | } 47 | 48 | static json::array_t as_array(const json& val) { 49 | if (val.type() != json::value_t::array) throw std::bad_cast(); 50 | return val.get(); 51 | } 52 | 53 | static int64_t as_integer(const json& val) { 54 | switch (val.type()) { 55 | case json::value_t::number_integer: 56 | case json::value_t::number_unsigned: return val.get(); 57 | default: throw std::bad_cast(); 58 | } 59 | } 60 | 61 | static bool as_boolean(const json& val) { 62 | if (val.type() != json::value_t::boolean) throw std::bad_cast(); 63 | return val.get(); 64 | } 65 | 66 | static double as_number(const json& val) { 67 | if (val.type() != json::value_t::number_float) throw std::bad_cast(); 68 | return val.get(); 69 | } 70 | 71 | static bool parse(json& val, std::string str) { 72 | val = json::parse(str.begin(), str.end()); 73 | return true; 74 | } 75 | 76 | static std::string serialize(const json& val) { return val.dump(); } 77 | }; 78 | } // namespace traits 79 | } // namespace jwt 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_OPEN_SOURCE_PARSERS_JSONCPP_DEFAULTS_H 2 | #define JWT_CPP_OPEN_SOURCE_PARSERS_JSONCPP_DEFAULTS_H 3 | 4 | #ifndef JWT_DISABLE_PICOJSON 5 | #define JWT_DISABLE_PICOJSON 6 | #endif 7 | 8 | #include "traits.h" 9 | 10 | namespace jwt { 11 | /** 12 | * \brief a class to store a generic [jsoncpp](https://github.com/open-source-parsers/jsoncpp) value as claim 13 | * 14 | * This type is the specialization of the \ref basic_claim class which 15 | * uses the standard template types. 16 | */ 17 | using claim = basic_claim; 18 | 19 | /** 20 | * Create a verifier using the default clock 21 | * \return verifier instance 22 | */ 23 | inline verifier verify() { 24 | return verify(default_clock{}); 25 | } 26 | 27 | /** 28 | * Create a builder using the default clock 29 | * \return builder instance to create a new token 30 | */ 31 | inline builder create() { 32 | return builder(default_clock{}); 33 | } 34 | 35 | #ifndef JWT_DISABLE_BASE64 36 | /** 37 | * Decode a token 38 | * \param token Token to decode 39 | * \return Decoded token 40 | * \throw std::invalid_argument Token is not in correct format 41 | * \throw std::runtime_error Base64 decoding failed or invalid json 42 | */ 43 | inline decoded_jwt decode(const std::string& token) { 44 | return decoded_jwt(token); 45 | } 46 | #endif 47 | 48 | /** 49 | * Decode a token 50 | * \tparam Decode is callable, taking a string_type and returns a string_type. 51 | * It should ensure the padding of the input and then base64url decode and 52 | * return the results. 53 | * \param token Token to decode 54 | * \param decode The token to parse 55 | * \return Decoded token 56 | * \throw std::invalid_argument Token is not in correct format 57 | * \throw std::runtime_error Base64 decoding failed or invalid json 58 | */ 59 | template 60 | decoded_jwt decode(const std::string& token, Decode decode) { 61 | return decoded_jwt(token, decode); 62 | } 63 | 64 | /** 65 | * Parse a jwk 66 | * \param token JWK Token to parse 67 | * \return Parsed JWK 68 | * \throw std::runtime_error Token is not in correct format 69 | */ 70 | inline jwk 71 | parse_jwk(const traits::open_source_parsers_jsoncpp::string_type& token) { 72 | return jwk(token); 73 | } 74 | 75 | /** 76 | * Parse a jwks 77 | * \param token JWKs Token to parse 78 | * \return Parsed JWKs 79 | * \throw std::runtime_error Token is not in correct format 80 | */ 81 | inline jwks 82 | parse_jwks(const traits::open_source_parsers_jsoncpp::string_type& token) { 83 | return jwks(token); 84 | } 85 | 86 | /** 87 | * This type is the specialization of the \ref verify_ops::verify_context class which 88 | * uses the standard template types. 89 | */ 90 | using verify_context = verify_ops::verify_context; 91 | } // namespace jwt 92 | 93 | #endif // JWT_CPP_OPEN_SOURCE_PARSERS_JSONCPP_DEFAULTS_H 94 | -------------------------------------------------------------------------------- /include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h: -------------------------------------------------------------------------------- 1 | #ifndef JWT_CPP_JSONCPP_TRAITS_H 2 | #define JWT_CPP_JSONCPP_TRAITS_H 3 | 4 | #include "jwt-cpp/jwt.h" 5 | #include "json/json.h" 6 | 7 | namespace jwt { 8 | /** 9 | * \brief Namespace containing all the json_trait implementations for a jwt::basic_claim. 10 | */ 11 | namespace traits { 12 | /// basic_claim's JSON trait implementation for jsoncpp 13 | struct open_source_parsers_jsoncpp { 14 | using value_type = Json::Value; 15 | using string_type = std::string; 16 | class array_type : public Json::Value { 17 | public: 18 | using value_type = Json::Value; 19 | 20 | array_type() = default; 21 | array_type(const array_type&) = default; 22 | explicit array_type(const Json::Value& o) : Json::Value(o) {} 23 | array_type(array_type&&) = default; 24 | explicit array_type(Json::Value&& o) : Json::Value(o) {} 25 | template 26 | array_type(Iterator begin, Iterator end) { 27 | for (Iterator it = begin; it != end; ++it) { 28 | Json::Value value; 29 | value = *it; 30 | this->append(value); 31 | } 32 | } 33 | ~array_type() = default; 34 | array_type& operator=(const array_type& o) = default; 35 | array_type& operator=(array_type&& o) noexcept = default; 36 | 37 | value_type const& front() const { return this->operator[](0U); } 38 | }; 39 | using number_type = double; 40 | using integer_type = Json::Value::Int; 41 | using boolean_type = bool; 42 | class object_type : public Json::Value { 43 | public: 44 | using key_type = std::string; 45 | using mapped_type = Json::Value; 46 | using size_type = size_t; 47 | 48 | object_type() = default; 49 | object_type(const object_type&) = default; 50 | explicit object_type(const Json::Value& o) : Json::Value(o) {} 51 | object_type(object_type&&) = default; 52 | explicit object_type(Json::Value&& o) : Json::Value(o) {} 53 | ~object_type() = default; 54 | object_type& operator=(const object_type& o) = default; 55 | object_type& operator=(object_type&& o) noexcept = default; 56 | 57 | // Add missing C++11 element access 58 | const mapped_type& at(const key_type& key) const { 59 | Json::Value const* found = find(key.data(), key.data() + key.length()); 60 | if (!found) throw std::out_of_range("invalid key"); 61 | return *found; 62 | } 63 | 64 | size_type count(const key_type& key) const { return this->isMember(key) ? 1 : 0; } 65 | }; 66 | 67 | // Translation between the implementation notion of type, to the jwt::json::type equivilant 68 | static jwt::json::type get_type(const value_type& val) { 69 | using jwt::json::type; 70 | 71 | if (val.isArray()) 72 | return type::array; 73 | else if (val.isString()) 74 | return type::string; 75 | // Order is important https://github.com/Thalhammer/jwt-cpp/pull/320#issuecomment-1865322511 76 | else if (val.isInt()) 77 | return type::integer; 78 | else if (val.isNumeric()) 79 | return type::number; 80 | else if (val.isBool()) 81 | return type::boolean; 82 | else if (val.isObject()) 83 | return type::object; 84 | 85 | throw std::logic_error("invalid type"); 86 | } 87 | 88 | static integer_type as_integer(const value_type& val) { 89 | switch (val.type()) { 90 | case Json::intValue: return val.asInt64(); 91 | case Json::uintValue: return static_cast(val.asUInt64()); 92 | default: throw std::bad_cast(); 93 | } 94 | } 95 | 96 | static boolean_type as_boolean(const value_type& val) { 97 | if (!val.isBool()) throw std::bad_cast(); 98 | return val.asBool(); 99 | } 100 | 101 | static number_type as_number(const value_type& val) { 102 | if (!val.isNumeric()) throw std::bad_cast(); 103 | return val.asDouble(); 104 | } 105 | 106 | static string_type as_string(const value_type& val) { 107 | if (!val.isString()) throw std::bad_cast(); 108 | return val.asString(); 109 | } 110 | 111 | static object_type as_object(const value_type& val) { 112 | if (!val.isObject()) throw std::bad_cast(); 113 | return object_type(val); 114 | } 115 | 116 | static array_type as_array(const value_type& val) { 117 | if (!val.isArray()) throw std::bad_cast(); 118 | return array_type(val); 119 | } 120 | 121 | static bool parse(value_type& val, string_type str) { 122 | Json::CharReaderBuilder builder; 123 | const std::unique_ptr reader(builder.newCharReader()); 124 | 125 | return reader->parse(reinterpret_cast(str.c_str()), 126 | reinterpret_cast(str.c_str() + str.size()), &val, nullptr); 127 | } 128 | 129 | static string_type serialize(const value_type& val) { 130 | Json::StreamWriterBuilder builder; 131 | builder["commentStyle"] = "None"; 132 | builder["indentation"] = ""; 133 | std::unique_ptr writer(builder.newStreamWriter()); 134 | return Json::writeString(builder, val); 135 | } 136 | }; 137 | } // namespace traits 138 | } // namespace jwt 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /nuget/jwt-cpp.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jwt-cpp 5 | 0.7.1 6 | Thalhammer; prince-chrismc 7 | Thalhammer; prince-chrismc 8 | https://github.com/Thalhammer/jwt-cpp 9 | JWT++ is a header only library for creating and validating JSON Web Tokens in C++11. This library supports all the algorithms defined by the JWT specifications, and the modular design allows to easily add additional algorithms without any problems. In the name of flexibility and extensibility, jwt-cpp supports OpenSSL, LibreSSL, and wolfSSL. And there is no hard dependency on a JSON library. 10 | 11 | Supporting OpenSSL 3.0.0, WolfSSL, Hunter CMake, Boost.JSON, JWKs, ES256K. 12 | MIT 13 | Copyright (c) 2018 Dominik Thalhammer 14 | JWT++: JSON Web Tokens in C++11 15 | JWT++; a header only library for creating and validating JSON Web Tokens in C++11. 16 | JWT, json, web, token, C++, header-only 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /nuget/jwt-cpp.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)..\..\lib\native\include\;%(AdditionalIncludeDirectories) 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/BaseTest.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/base.h" 2 | #include 3 | 4 | TEST(BaseTest, Base64Index) { 5 | ASSERT_EQ(0, jwt::alphabet::index(jwt::alphabet::base64::rdata(), 'A')); 6 | ASSERT_EQ(32, jwt::alphabet::index(jwt::alphabet::base64::rdata(), 'g')); 7 | ASSERT_EQ(62, jwt::alphabet::index(jwt::alphabet::base64::rdata(), '+')); 8 | 9 | std::size_t index = 0; 10 | for (auto c : jwt::alphabet::base64::data()) { 11 | ASSERT_EQ(index, jwt::alphabet::index(jwt::alphabet::base64::rdata(), c)); 12 | index++; 13 | } 14 | 15 | std::size_t noBaseCharCount = 0; 16 | for (std::size_t i = 0; i < jwt::alphabet::base64::rdata().size(); i++) { 17 | if (jwt::alphabet::base64::rdata().at(i) == -1) { noBaseCharCount++; } 18 | } 19 | ASSERT_EQ(jwt::alphabet::base64::rdata().size() - jwt::alphabet::base64::data().size(), noBaseCharCount); 20 | } 21 | 22 | TEST(BaseTest, Base64URLIndex) { 23 | ASSERT_EQ(0, jwt::alphabet::index(jwt::alphabet::base64url::rdata(), 'A')); 24 | ASSERT_EQ(32, jwt::alphabet::index(jwt::alphabet::base64url::rdata(), 'g')); 25 | ASSERT_EQ(62, jwt::alphabet::index(jwt::alphabet::base64url::rdata(), '-')); 26 | 27 | std::size_t index = 0; 28 | for (auto c : jwt::alphabet::base64url::data()) { 29 | ASSERT_EQ(index, jwt::alphabet::index(jwt::alphabet::base64url::rdata(), c)); 30 | index++; 31 | } 32 | 33 | std::size_t noBaseCharCount = 0; 34 | for (std::size_t i = 0; i < jwt::alphabet::base64url::rdata().size(); i++) { 35 | if (jwt::alphabet::base64url::rdata().at(i) == -1) { noBaseCharCount++; } 36 | } 37 | ASSERT_EQ(jwt::alphabet::base64url::rdata().size() - jwt::alphabet::base64url::data().size(), noBaseCharCount); 38 | } 39 | 40 | TEST(BaseTest, BaseDetailsCountPadding) { 41 | using jwt::base::details::padding; 42 | ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC", {"~"})); 43 | ASSERT_EQ((padding{3, 3}), jwt::base::details::count_padding("ABC~~~", {"~"})); 44 | ASSERT_EQ((padding{5, 5}), jwt::base::details::count_padding("ABC~~~~~", {"~"})); 45 | 46 | ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC", {"~", "!"})); 47 | ASSERT_EQ((padding{1, 1}), jwt::base::details::count_padding("ABC!", {"~", "!"})); 48 | ASSERT_EQ((padding{1, 1}), jwt::base::details::count_padding("ABC~", {"~", "!"})); 49 | ASSERT_EQ((padding{3, 3}), jwt::base::details::count_padding("ABC~~!", {"~", "!"})); 50 | ASSERT_EQ((padding{3, 3}), jwt::base::details::count_padding("ABC!~~", {"~", "!"})); 51 | ASSERT_EQ((padding{5, 5}), jwt::base::details::count_padding("ABC~~!~~", {"~", "!"})); 52 | 53 | ASSERT_EQ((padding{2, 6}), jwt::base::details::count_padding("MTIzNA%3d%3d", {"%3d", "%3D"})); 54 | ASSERT_EQ((padding{2, 6}), jwt::base::details::count_padding("MTIzNA%3d%3D", {"%3d", "%3D"})); 55 | ASSERT_EQ((padding{2, 6}), jwt::base::details::count_padding("MTIzNA%3D%3d", {"%3d", "%3D"})); 56 | ASSERT_EQ((padding{2, 6}), jwt::base::details::count_padding("MTIzNA%3D%3D", {"%3d", "%3D"})); 57 | 58 | // Some fake scenarios 59 | 60 | ASSERT_EQ(padding{}, jwt::base::details::count_padding("", {"~"})); 61 | ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC", {"~", "~~!"})); 62 | ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC!", {"~", "~~!"})); 63 | ASSERT_EQ((padding{1, 1}), jwt::base::details::count_padding("ABC~", {"~", "~~!"})); 64 | ASSERT_EQ((padding{1, 3}), jwt::base::details::count_padding("ABC~~!", {"~", "~~!"})); 65 | ASSERT_EQ((padding{2, 2}), jwt::base::details::count_padding("ABC!~~", {"~", "~~!"})); 66 | ASSERT_EQ((padding{3, 5}), jwt::base::details::count_padding("ABC~~!~~", {"~", "~~!"})); 67 | ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC~~!~~", {})); 68 | } 69 | 70 | TEST(BaseTest, Base64Decode) { 71 | ASSERT_EQ("1", jwt::base::decode("MQ==")); 72 | ASSERT_EQ("12", jwt::base::decode("MTI=")); 73 | ASSERT_EQ("123", jwt::base::decode("MTIz")); 74 | ASSERT_EQ("1234", jwt::base::decode("MTIzNA==")); 75 | } 76 | 77 | TEST(BaseTest, Base64DecodeURL) { 78 | ASSERT_EQ("1", jwt::base::decode("MQ%3d%3d")); 79 | ASSERT_EQ("12", jwt::base::decode("MTI%3d")); 80 | ASSERT_EQ("123", jwt::base::decode("MTIz")); 81 | ASSERT_EQ("1234", jwt::base::decode("MTIzNA%3d%3d")); 82 | } 83 | 84 | TEST(BaseTest, Base64DecodeURLCaseInsensitive) { 85 | ASSERT_EQ("1", jwt::base::decode("MQ%3d%3d")); 86 | ASSERT_EQ("1", jwt::base::decode("MQ%3D%3d")); 87 | ASSERT_EQ("1", jwt::base::decode("MQ%3d%3D")); 88 | ASSERT_EQ("12", jwt::base::decode("MTI%3d")); 89 | ASSERT_EQ("123", jwt::base::decode("MTIz")); 90 | ASSERT_EQ("1234", jwt::base::decode("MTIzNA%3d%3d")); 91 | ASSERT_EQ("1234", jwt::base::decode("MTIzNA%3D%3D")); 92 | } 93 | 94 | TEST(BaseTest, Base64Encode) { 95 | ASSERT_EQ("MQ==", jwt::base::encode("1")); 96 | ASSERT_EQ("MTI=", jwt::base::encode("12")); 97 | ASSERT_EQ("MTIz", jwt::base::encode("123")); 98 | ASSERT_EQ("MTIzNA==", jwt::base::encode("1234")); 99 | } 100 | 101 | TEST(BaseTest, Base64EncodeURL) { 102 | ASSERT_EQ("MQ%3d%3d", jwt::base::encode("1")); 103 | ASSERT_EQ("MTI%3d", jwt::base::encode("12")); 104 | ASSERT_EQ("MTIz", jwt::base::encode("123")); 105 | ASSERT_EQ("MTIzNA%3d%3d", jwt::base::encode("1234")); 106 | } 107 | 108 | TEST(BaseTest, Base64Pad) { 109 | ASSERT_EQ("MQ==", jwt::base::pad("MQ")); 110 | ASSERT_EQ("MTI=", jwt::base::pad("MTI")); 111 | ASSERT_EQ("MTIz", jwt::base::pad("MTIz")); 112 | ASSERT_EQ("MTIzNA==", jwt::base::pad("MTIzNA")); 113 | } 114 | 115 | TEST(BaseTest, Base64PadURL) { 116 | ASSERT_EQ("MQ%3d%3d", jwt::base::pad("MQ")); 117 | ASSERT_EQ("MTI%3d", jwt::base::pad("MTI")); 118 | ASSERT_EQ("MTIz", jwt::base::pad("MTIz")); 119 | ASSERT_EQ("MTIzNA%3d%3d", jwt::base::pad("MTIzNA")); 120 | } 121 | 122 | TEST(BaseTest, Base64Trim) { 123 | ASSERT_EQ("MQ", jwt::base::trim("MQ==")); 124 | ASSERT_EQ("MTI", jwt::base::trim("MTI=")); 125 | ASSERT_EQ("MTIz", jwt::base::trim("MTIz")); 126 | ASSERT_EQ("MTIzNA", jwt::base::trim("MTIzNA==")); 127 | } 128 | 129 | TEST(BaseTest, Base64TrimURL) { 130 | ASSERT_EQ("MQ", jwt::base::trim("MQ%3d%3d")); 131 | ASSERT_EQ("MTI", jwt::base::trim("MTI%3d")); 132 | ASSERT_EQ("MTIz", jwt::base::trim("MTIz")); 133 | ASSERT_EQ("MTIzNA", jwt::base::trim("MTIzNA%3d%3d")); 134 | } 135 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(JWT_DISABLE_BASE64) 2 | message(FATAL_ERROR "Tests requires the base64 support to be enabled!") 3 | endif() 4 | 5 | if(JWT_DISABLE_PICOJSON) 6 | message(FATAL_ERROR "Tests requires the picojson support to be enabled!") 7 | endif() 8 | 9 | include(GoogleTest) 10 | if(HUNTER_ENABLED) 11 | hunter_add_package(GTest) 12 | endif() 13 | 14 | find_package(GTest) 15 | if(NOT TARGET GTest::gtest_main) 16 | message(STATUS "jwt-cpp: using FetchContent for GTest") 17 | include(FetchContent) 18 | fetchcontent_declare(googletest 19 | URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip) 20 | # https://google.github.io/googletest/quickstart-cmake.html 21 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 22 | fetchcontent_makeavailable(googletest) 23 | endif() 24 | 25 | set(TEST_SOURCES 26 | ${CMAKE_CURRENT_SOURCE_DIR}/BaseTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ClaimTest.cpp 27 | ${CMAKE_CURRENT_SOURCE_DIR}/Keys.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HelperTest.cpp 28 | ${CMAKE_CURRENT_SOURCE_DIR}/TestMain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TokenFormatTest.cpp 29 | ${CMAKE_CURRENT_SOURCE_DIR}/TokenTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/JwksTest.cpp 30 | ${CMAKE_CURRENT_SOURCE_DIR}/OpenSSLErrorTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/traits/NlohmannTest.cpp) 31 | 32 | find_package(jsoncons CONFIG) 33 | if(TARGET jsoncons) 34 | list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/JsonconsTest.cpp) 35 | endif() 36 | 37 | include("private-find-boost-json") 38 | if(TARGET boost_json) 39 | list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/BoostJsonTest.cpp) 40 | endif() 41 | 42 | find_package(jsoncpp CONFIG) 43 | if(TARGET jsoncpp_static) 44 | list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/OspJsoncppTest.cpp) 45 | endif() 46 | 47 | add_executable(jwt-cpp-test ${TEST_SOURCES}) 48 | 49 | # NOTE: Don't use space inside a generator expression here, because the function prematurely breaks the expression into 50 | # multiple lines. https://cmake.org/pipermail/cmake/2018-April/067422.html 51 | set(JWT_TESTER_GCC_FLAGS -Wall -Wextra -Wpedantic -ggdb) 52 | set(JWT_TESTER_CLANG_FLAGS -Weverything -Wno-c++98-compat -Wno-global-constructors -Wno-weak-vtables) 53 | target_compile_options( 54 | jwt-cpp-test PRIVATE $<$:/W4> $<$:${JWT_TESTER_GCC_FLAGS}> 55 | $<$:${JWT_TESTER_CLANG_FLAGS}>) 56 | if(HUNTER_ENABLED) 57 | target_link_libraries(jwt-cpp-test PRIVATE GTest::gtest GTest::gtest_main) 58 | # Define a compile define to bypass openssl error tests 59 | target_compile_definitions(jwt-cpp-test PRIVATE HUNTER_ENABLED=1) 60 | else() 61 | # https://github.com/google/googletest/blob/eb80f759d595874a5e905a3342bd8e2af4c0a12d/googletest/README.md?plain=1#L62-L64 62 | target_link_libraries(jwt-cpp-test PRIVATE GTest::gtest_main) 63 | if(TARGET jsoncons) 64 | target_link_libraries(jwt-cpp-test PRIVATE jsoncons) 65 | endif() 66 | if(TARGET boost_json) 67 | target_link_libraries(jwt-cpp-test PRIVATE boost_json) 68 | endif() 69 | if(TARGET jsoncpp_static) 70 | target_link_libraries(jwt-cpp-test PRIVATE jsoncpp_static) 71 | endif() 72 | endif() 73 | target_link_libraries(jwt-cpp-test PRIVATE jwt-cpp nlohmann_json::nlohmann_json 74 | $<$>:${CMAKE_DL_LIBS}>) 75 | 76 | gtest_discover_tests(jwt-cpp-test) 77 | add_custom_target(jwt-cpp-test-run COMMAND jwt-cpp-test) 78 | 79 | if(JWT_ENABLE_COVERAGE) 80 | include("code-coverage") 81 | setup_coverage(jwt-cpp-test) 82 | set(COVERAGE_EXCLUDES "/usr/**" "/home/*/.conan/**" "*test*" "*build*" "**/nlohmann/json.hpp" 83 | "**/picojson/picojson.h" "*boost*" "*jsoncons*") 84 | setup_target_for_coverage_lcov(NAME coverage EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/jwt-cpp-test) 85 | endif() 86 | -------------------------------------------------------------------------------- /tests/CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 25, 6 | "patch": 0 7 | }, 8 | "include": [ 9 | "../cmake/CMakePresets.json" 10 | ], 11 | "configurePresets": [ 12 | { 13 | "name": "linux", 14 | "hidden": true, 15 | "description": "This is only available on Linux", 16 | "condition": { 17 | "type": "equals", 18 | "lhs": "${hostSystemName}", 19 | "rhs": "Linux" 20 | } 21 | }, 22 | { 23 | "name": "coverage", 24 | "inherits": ["debug", "linux"], 25 | "displayName": "Coverage", 26 | "description": "This is only available on Linux", 27 | "cacheVariables": { 28 | "JWT_BUILD_TESTS": "ON", 29 | "JWT_ENABLE_COVERAGE": "ON" 30 | } 31 | }, 32 | { 33 | "name": "unit-tests", 34 | "inherits": ["debug"], 35 | "displayName": "Unit Tests", 36 | "cacheVariables": { 37 | "JWT_BUILD_TESTS": "ON" 38 | } 39 | }, 40 | { 41 | "name": "ci-fuzzing", 42 | "inherits": ["debug", "linux"], 43 | "displayName": "Fuzz Testing", 44 | "description": "This is only available on Linux with Clang", 45 | "cacheVariables": { 46 | "JWT_ENABLE_FUZZING": "ON", 47 | "CMAKE_C_COMPILER": "clang", 48 | "CMAKE_CXX_COMPILER": "clang++" 49 | } 50 | }, 51 | { 52 | "name": "ci-asan", 53 | "inherits": ["debug", "linux"], 54 | "displayName": "Address/Leak Sanitizer", 55 | "description": "This is only available on Linux", 56 | "environment": { 57 | "ASAN_FLAGS": "-fsanitize=address -fsanitize=leak", 58 | "ASAN_OPTIONS": "check_initialization_order=true:detect_stack_use_after_return=true:fast_unwind_on_malloc=0" 59 | }, 60 | "cacheVariables": { 61 | "JWT_BUILD_EXAMPLES": "ON", 62 | "JWT_BUILD_TESTS": "ON", 63 | "CMAKE_C_FLAGS": "$env{ASAN_FLAGS}", 64 | "CMAKE_CXX_FLAGS": "$env{ASAN_FLAGS}", 65 | "CMAKE_EXE_LINKER_FLAGS": "$env{ASAN_FLAGS}", 66 | "CMAKE_MODULE_LINKER_FLAGS": "$env{ASAN_FLAGS}" 67 | } 68 | }, 69 | { 70 | "name": "ci-ubsan", 71 | "inherits": ["debug", "linux"], 72 | "displayName": "Undefined Behavior Sanitizer", 73 | "description": "This is only available on Linux", 74 | "environment": { 75 | "UBSAN_FLAGS": "-fsanitize=undefined -fsanitize=return -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize-recover=all", 76 | "UBSAN_OPTIONS": "print_stacktrace=1" 77 | }, 78 | "cacheVariables": { 79 | "JWT_BUILD_EXAMPLES": "ON", 80 | "JWT_BUILD_TESTS": "ON", 81 | "CMAKE_C_FLAGS": "$env{UBSAN_FLAGS}", 82 | "CMAKE_CXX_FLAGS": "$env{UBSAN_FLAGS}", 83 | "CMAKE_EXE_LINKER_FLAGS": "$env{UBSAN_FLAGS}", 84 | "CMAKE_MODULE_LINKER_FLAGS": "$env{UBSAN_FLAGS}" 85 | } 86 | } 87 | ], 88 | "buildPresets": [ 89 | { 90 | "name": "coverage", 91 | "configurePreset": "coverage", 92 | "configuration": "Debug", 93 | "targets": [ 94 | "jwt-cpp-test", 95 | "coverage" 96 | ] 97 | }, 98 | { 99 | "name": "unit-tests", 100 | "configurePreset": "unit-tests", 101 | "configuration": "Debug", 102 | "targets": [ 103 | "jwt-cpp-test" 104 | ] 105 | }, 106 | { 107 | "name": "ci-fuzzing", 108 | "configurePreset": "ci-fuzzing", 109 | "targets": [ 110 | "jwt-cpp-fuzz-BaseEncodeFuzz", 111 | "jwt-cpp-fuzz-BaseDecodeFuzz", 112 | "jwt-cpp-fuzz-TokenDecodeFuzz" 113 | ] 114 | }, 115 | { 116 | "name": "ci-asan", 117 | "configurePreset": "ci-asan", 118 | "targets": [ 119 | "rsa-create", 120 | "rsa-verify", 121 | "jwks-verify", 122 | "jwt-cpp-test" 123 | ] 124 | }, 125 | { 126 | "name": "ci-ubsan", 127 | "configurePreset": "ci-ubsan", 128 | "targets": [ 129 | "rsa-create", 130 | "rsa-verify", 131 | "jwks-verify", 132 | "jwt-cpp-test" 133 | ] 134 | } 135 | ], 136 | "testPresets": [ 137 | { 138 | "name": "unit-tests", 139 | "description": "Run the unit tests", 140 | "configurePreset": "unit-tests", 141 | "execution": { 142 | "noTestsAction": "error" 143 | } 144 | } 145 | ] 146 | } -------------------------------------------------------------------------------- /tests/ClaimTest.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/jwt.h" 2 | #include 3 | 4 | TEST(ClaimTest, AudienceAsString) { 5 | std::string const token = 6 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0In0.WZnM3SIiSRHsbO3O7Z2bmIzTJ4EC32HRBKfLznHhrh4"; 7 | auto decoded = jwt::decode(token); 8 | 9 | ASSERT_TRUE(decoded.has_algorithm()); 10 | ASSERT_TRUE(decoded.has_type()); 11 | ASSERT_FALSE(decoded.has_content_type()); 12 | ASSERT_FALSE(decoded.has_key_id()); 13 | ASSERT_FALSE(decoded.has_issuer()); 14 | ASSERT_FALSE(decoded.has_subject()); 15 | ASSERT_TRUE(decoded.has_audience()); 16 | ASSERT_FALSE(decoded.has_expires_at()); 17 | ASSERT_FALSE(decoded.has_not_before()); 18 | ASSERT_FALSE(decoded.has_issued_at()); 19 | ASSERT_FALSE(decoded.has_id()); 20 | 21 | ASSERT_EQ("HS256", decoded.get_algorithm()); 22 | ASSERT_EQ("JWT", decoded.get_type()); 23 | auto aud = decoded.get_audience(); 24 | ASSERT_EQ(1, aud.size()); 25 | ASSERT_EQ("test", *aud.begin()); 26 | } 27 | 28 | TEST(ClaimTest, SetAudienceAsString) { 29 | auto token = jwt::create().set_type("JWT").set_audience("test").sign(jwt::algorithm::hs256("test")); 30 | ASSERT_EQ("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0In0.ny5Fa0vzAg7tNL95KWg_ecBNd3XP3tdAzq0SFA6diY4", 31 | token); 32 | } 33 | 34 | TEST(ClaimTest, AudienceAsSet) { 35 | std::string const token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJhdWQiOlsidGVzdCIsInRlc3QyIl19."; 36 | auto decoded = jwt::decode(token); 37 | 38 | ASSERT_TRUE(decoded.has_algorithm()); 39 | ASSERT_TRUE(decoded.has_type()); 40 | ASSERT_FALSE(decoded.has_content_type()); 41 | ASSERT_FALSE(decoded.has_key_id()); 42 | ASSERT_FALSE(decoded.has_issuer()); 43 | ASSERT_FALSE(decoded.has_subject()); 44 | ASSERT_TRUE(decoded.has_audience()); 45 | ASSERT_FALSE(decoded.has_expires_at()); 46 | ASSERT_FALSE(decoded.has_not_before()); 47 | ASSERT_FALSE(decoded.has_issued_at()); 48 | ASSERT_FALSE(decoded.has_id()); 49 | 50 | ASSERT_EQ("none", decoded.get_algorithm()); 51 | ASSERT_EQ("JWT", decoded.get_type()); 52 | auto aud = decoded.get_audience(); 53 | ASSERT_EQ(2, aud.size()); 54 | ASSERT_TRUE(aud.count("test") > 0); 55 | ASSERT_TRUE(aud.count("test2") > 0); 56 | } 57 | 58 | TEST(ClaimTest, SetAudienceAsSet) { 59 | auto token = jwt::create() 60 | .set_type("JWT") 61 | .set_audience({{picojson::value("test"), picojson::value("test2")}}) 62 | .sign(jwt::algorithm::none{}); 63 | ASSERT_EQ("eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJhdWQiOlsidGVzdCIsInRlc3QyIl19.", token); 64 | } 65 | 66 | TEST(ClaimTest, SetArray) { 67 | std::vector vect = {100, 20, 10}; 68 | auto token = 69 | jwt::create().set_payload_claim("test", jwt::claim(vect.begin(), vect.end())).sign(jwt::algorithm::none{}); 70 | ASSERT_EQ(token, "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."); 71 | } 72 | 73 | TEST(ClaimTest, SetObject) { 74 | std::istringstream iss{"{\"api-x\": [1]}"}; 75 | jwt::claim object; 76 | iss >> object; 77 | ASSERT_EQ(object.get_type(), jwt::json::type::object); 78 | 79 | auto token = jwt::create().set_payload_claim("namespace", object).sign(jwt::algorithm::hs256("test")); 80 | ASSERT_EQ(token, 81 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"); 82 | } 83 | 84 | TEST(ClaimTest, SetAlgorithm) { 85 | auto token = jwt::create().set_algorithm("test").sign(jwt::algorithm::none{}); 86 | 87 | auto decoded_token = jwt::decode(token); 88 | ASSERT_EQ(decoded_token.get_algorithm(), "test"); 89 | } 90 | 91 | TEST(ClaimTest, AsInt) { 92 | jwt::claim c(picojson::value(static_cast(10))); 93 | ASSERT_EQ(c.as_integer(), 10); 94 | } 95 | 96 | TEST(ClaimTest, AsDate) { 97 | jwt::claim c(picojson::value(static_cast(10))); 98 | ASSERT_EQ(c.as_date(), std::chrono::system_clock::from_time_t(10)); 99 | } 100 | 101 | TEST(ClaimTest, PicoJSONTraitsAccessorsThrow) { 102 | jwt::traits::kazuho_picojson::value_type val; 103 | ASSERT_THROW(jwt::traits::kazuho_picojson::as_array(val), std::bad_cast); 104 | ASSERT_THROW(jwt::traits::kazuho_picojson::as_boolean(val), std::bad_cast); 105 | ASSERT_THROW(jwt::traits::kazuho_picojson::as_integer(val), std::bad_cast); 106 | ASSERT_THROW(jwt::traits::kazuho_picojson::as_number(val), std::bad_cast); 107 | ASSERT_THROW(jwt::traits::kazuho_picojson::as_object(val), std::bad_cast); 108 | ASSERT_THROW(jwt::traits::kazuho_picojson::as_string(val), std::bad_cast); 109 | ASSERT_THROW(jwt::traits::kazuho_picojson::get_type(val), std::logic_error); 110 | } 111 | 112 | TEST(ClaimTest, PicoJSONTraitsAsBool) { 113 | jwt::traits::kazuho_picojson::value_type val(true); 114 | ASSERT_EQ(jwt::traits::kazuho_picojson::as_boolean(val), true); 115 | ASSERT_EQ(jwt::traits::kazuho_picojson::get_type(val), jwt::json::type::boolean); 116 | } 117 | 118 | TEST(ClaimTest, PicoJSONTraitsAsDouble) { 119 | jwt::traits::kazuho_picojson::value_type val(10.0); 120 | ASSERT_EQ(jwt::traits::kazuho_picojson::as_number(val), (int)10); 121 | ASSERT_EQ(jwt::traits::kazuho_picojson::get_type(val), jwt::json::type::number); 122 | } 123 | 124 | TEST(ClaimTest, MapOfClaim) { 125 | using map = jwt::details::map_of_claims; 126 | ASSERT_THROW(map::parse_claims(R"##(__ not json __)##"), jwt::error::invalid_json_exception); 127 | const map claims{ 128 | map::parse_claims(R"##({ "array": [1], "string" : "hello world", "number": 9.9, "bool": true})##")}; 129 | 130 | ASSERT_TRUE(claims.has_claim("array")); 131 | ASSERT_TRUE(claims.has_claim("string")); 132 | ASSERT_TRUE(claims.has_claim("number")); 133 | ASSERT_TRUE(claims.has_claim("bool")); 134 | ASSERT_FALSE(claims.has_claim("__missing__")); 135 | 136 | ASSERT_EQ(map::basic_claim_t{claims.get_claim("array").as_array().at(0)}.as_integer(), (int)1); 137 | ASSERT_EQ(claims.get_claim("string").as_string(), "hello world"); 138 | ASSERT_EQ(claims.get_claim("number").as_number(), 9.9); 139 | ASSERT_EQ(claims.get_claim("bool").as_boolean(), true); 140 | ASSERT_THROW(claims.get_claim("__missing__"), jwt::error::claim_not_present_exception); 141 | } 142 | -------------------------------------------------------------------------------- /tests/TestMain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char* argv[]) { 4 | ::testing::InitGoogleTest(&argc, argv); 5 | return RUN_ALL_TESTS(); 6 | } 7 | -------------------------------------------------------------------------------- /tests/TokenFormatTest.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/jwt.h" 2 | #include 3 | 4 | TEST(TokenFormatTest, MissingDot) { 5 | ASSERT_THROW(jwt::decode("eyJhbGciOiJub25lIiwidHlwIjoiSldTIn0.eyJpc3MiOiJhdXRoMCJ9"), std::invalid_argument); 6 | ASSERT_THROW(jwt::decode("eyJhbGciOiJub25lIiwidHlwIjoiSldTIn0eyJpc3MiOiJhdXRoMCJ9."), std::invalid_argument); 7 | ASSERT_THROW(jwt::decode("eyJhbGciOiJub25lIiwidHlwIjoiSldTIn0eyJpc3MiOiJhdXRoMCJ9"), std::invalid_argument); 8 | } 9 | 10 | TEST(TokenFormatTest, InvalidChar) { 11 | ASSERT_THROW(jwt::decode("eyJhbGciOiJub25lIiwidHlwIjoiSldTIn0().eyJpc3MiOiJhdXRoMCJ9."), std::runtime_error); 12 | } 13 | 14 | TEST(TokenFormatTest, InvalidJSON) { 15 | ASSERT_THROW(jwt::decode("YXsiYWxnIjoibm9uZSIsInR5cCI6IkpXUyJ9YQ.eyJpc3MiOiJhdXRoMCJ9."), std::runtime_error); 16 | } 17 | 18 | #include "jwt-cpp/traits/nlohmann-json/traits.h" 19 | 20 | TEST(TokenFormatTest, GitHubIssue341) { 21 | std::string const token = 22 | "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjYXV0aDAiLCJleHAiOjE3MTMzODgxNjgsInN1YiI6InRlc3RfdXNlciJ9." 23 | "dlAk0mSWk1Clzfi1PMq7Omxun3EyEqh-AAu-fTkpabA67ZKenawAQhZO8glY93flukpJCqHLVtukaes6ZSOjGw"; 24 | auto decoded = jwt::decoded_jwt(token); 25 | 26 | ASSERT_TRUE(decoded.has_algorithm()); 27 | ASSERT_TRUE(decoded.has_type()); 28 | ASSERT_TRUE(decoded.has_issuer()); 29 | ASSERT_TRUE(decoded.has_subject()); 30 | 31 | ASSERT_EQ("ES256", decoded.get_algorithm()); 32 | ASSERT_EQ("JWT", decoded.get_type()); 33 | ASSERT_EQ("cauth0", decoded.get_issuer()); 34 | ASSERT_EQ("test_user", decoded.get_subject()); 35 | } 36 | -------------------------------------------------------------------------------- /tests/cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(jwt-cpp-installation-tests) 3 | 4 | set(TEST CACHE STRING "The test source file to be used") 5 | 6 | find_package(jwt-cpp 0.7.1 EXACT REQUIRED CONFIG) 7 | 8 | add_executable(test-project ${TEST}.cpp) 9 | target_link_libraries(test-project jwt-cpp::jwt-cpp) 10 | -------------------------------------------------------------------------------- /tests/cmake/base64-is-disabled.cpp: -------------------------------------------------------------------------------- 1 | #ifndef JWT_DISABLE_BASE64 2 | #error "This test expects 'JWT_DISABLE_BASE64' to be defined!" 3 | #endif 4 | 5 | #include "jwt-cpp/jwt.h" 6 | 7 | int main() { 8 | jwt::date date; 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/cmake/defaults-enabled.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/jwt.h" 2 | 3 | int main() { 4 | jwt::claim claim; 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/cmake/libressl-is-used.cpp: -------------------------------------------------------------------------------- 1 | #if !__has_include() 2 | #error "missing LibreSSL's TLS header!" 3 | #endif 4 | 5 | #include 6 | 7 | #include "jwt-cpp/jwt.h" 8 | 9 | int main() { 10 | tls_init(); 11 | jwt::date date; 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/cmake/picojson-is-disabled.cpp: -------------------------------------------------------------------------------- 1 | #ifndef JWT_DISABLE_PICOJSON 2 | #error "This test expects 'JWT_DISABLE_PICOJSON' to be defined!" 3 | #endif 4 | 5 | #include "jwt-cpp/jwt.h" 6 | 7 | int main() { 8 | jwt::date date; 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/cmake/wolfssl-is-used.cpp: -------------------------------------------------------------------------------- 1 | #if !__has_include() 2 | #error "missing wolfSSL's SSL header!" 3 | #endif 4 | 5 | // See https://github.com/Thalhammer/jwt-cpp/pull/352 6 | #ifndef EXTERNAL_OPTS_OPENVPN 7 | #error "missing wolfSSL's OPENSSL_EXTRA macro!" 8 | #endif 9 | 10 | #include "jwt-cpp/jwt.h" 11 | 12 | #include 13 | 14 | int main() { 15 | wolfSSL_library_init(); 16 | jwt::date date; 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /tests/fuzz/BaseDecodeFuzz.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" { 4 | 5 | int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { 6 | try { 7 | const auto bin = jwt::base::decode(std::string{(char*)Data, Size}); 8 | } catch (const std::runtime_error&) { 9 | // parse errors are ok, because input may be random bytes 10 | } 11 | return 0; // Non-zero return values are reserved for future use. 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/fuzz/BaseEncodeFuzz.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" { 4 | 5 | int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { 6 | jwt::base::encode(std::string{(char*)Data, Size}); 7 | return 0; // Non-zero return values are reserved for future use. 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/fuzz/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL Clang) 2 | message(FATAL_ERROR "Fuzzing is only available on Clang") 3 | endif() 4 | 5 | function(ADD_FUZZING_EXECUTABLE TARGET) 6 | add_executable(jwt-cpp-fuzz-${TARGET} "${TARGET}.cpp") 7 | target_compile_options( 8 | jwt-cpp-fuzz-${TARGET} PRIVATE -g -O1 -fsanitize=fuzzer,address,signed-integer-overflow,undefined 9 | -fno-omit-frame-pointer) 10 | target_link_options(jwt-cpp-fuzz-${TARGET} PRIVATE -fsanitize=fuzzer,address,signed-integer-overflow,undefined 11 | -fno-omit-frame-pointer) 12 | target_link_libraries(jwt-cpp-fuzz-${TARGET} PRIVATE jwt-cpp::jwt-cpp) 13 | endfunction() 14 | 15 | add_fuzzing_executable(BaseEncodeFuzz) 16 | add_custom_target(jwt-cpp-fuzz-BaseEncodeFuzz-run COMMAND jwt-cpp-fuzz-BaseEncodeFuzz -runs=100000) 17 | 18 | add_fuzzing_executable(BaseDecodeFuzz) 19 | add_custom_target(jwt-cpp-fuzz-BaseDecodeFuzz-run COMMAND jwt-cpp-fuzz-BaseDecodeFuzz -runs=100000 decode-corpus 20 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 21 | 22 | add_fuzzing_executable(TokenDecodeFuzz) 23 | add_custom_target(jwt-cpp-fuzz-TokenDecodeFuzz-run COMMAND jwt-cpp-fuzz-TokenDecodeFuzz -runs=100000 token-corpus 24 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 25 | -------------------------------------------------------------------------------- /tests/fuzz/TokenDecodeFuzz.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" { 4 | 5 | int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { 6 | try { 7 | // step 1: parse input 8 | const auto jwt1 = jwt::decode(std::string{(char*)Data, Size}); 9 | 10 | try { 11 | // step 2: round trip 12 | std::string s1 = jwt1.get_token(); 13 | const auto jwt2 = jwt::decode(s1); 14 | 15 | // tokens must match 16 | if (s1 != jwt2.get_token()) abort(); 17 | } catch (...) { 18 | // parsing raw data twice must not fail 19 | abort(); 20 | } 21 | } catch (...) { 22 | // parse errors are ok, because input may be random bytes 23 | } 24 | 25 | return 0; // Non-zero return values are reserved for future use. 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/fuzz/decode-corpus/086a3aa337038cac8a75a05131444f222e48aee8: -------------------------------------------------------------------------------- 1 | FMF= -------------------------------------------------------------------------------- /tests/fuzz/decode-corpus/8ebaef2304e91465585c8d7fcf4d9f939e08d6b4: -------------------------------------------------------------------------------- 1 | eCy -------------------------------------------------------------------------------- /tests/fuzz/decode-corpus/ba528234d9f6949ed9c9626c08a782f6e7c15b8b: -------------------------------------------------------------------------------- 1 | FF== -------------------------------------------------------------------------------- /tests/fuzz/decode-corpus/de1028a3fe87471f027522c3ed9ec02b8364a006: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thalhammer/jwt-cpp/0c676f86b7ec78fed88cbd1de259e1ed994779c9/tests/fuzz/decode-corpus/de1028a3fe87471f027522c3ed9ec02b8364a006 -------------------------------------------------------------------------------- /tests/fuzz/decode-corpus/e8f531caaa67cecb1c7b162f3e1d4e320d79befd: -------------------------------------------------------------------------------- 1 | eCyI -------------------------------------------------------------------------------- /tests/fuzz/token-corpus/9d891e731f75deae56884d79e9816736b7488080: -------------------------------------------------------------------------------- 1 | .. -------------------------------------------------------------------------------- /tests/fuzz/token-corpus/ff384e2421a333cd52f259cec14c7f790d595db9: -------------------------------------------------------------------------------- 1 | eyJhbGci.. -------------------------------------------------------------------------------- /tests/fuzz/token-corpus/valid-sample: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE -------------------------------------------------------------------------------- /tests/traits/BoostJsonTest.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/boost-json/traits.h" 2 | 3 | #include 4 | 5 | TEST(BoostJsonTest, BasicClaims) { 6 | const auto string = jwt::basic_claim(jwt::traits::boost_json::string_type("string")); 7 | ASSERT_EQ(string.get_type(), jwt::json::type::string); 8 | 9 | const auto array = 10 | jwt::basic_claim(std::set{"string", "string"}); 11 | ASSERT_EQ(array.get_type(), jwt::json::type::array); 12 | 13 | jwt::traits::boost_json::value_type jvi = 159816816; 14 | const auto integer = jwt::basic_claim(jvi); 15 | ASSERT_EQ(integer.get_type(), jwt::json::type::integer); 16 | } 17 | 18 | TEST(BoostJsonTest, AudienceAsString) { 19 | jwt::traits::boost_json::string_type token = 20 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0In0.WZnM3SIiSRHsbO3O7Z2bmIzTJ4EC32HRBKfLznHhrh4"; 21 | auto decoded = jwt::decode(token); 22 | 23 | ASSERT_TRUE(decoded.has_algorithm()); 24 | ASSERT_TRUE(decoded.has_type()); 25 | ASSERT_FALSE(decoded.has_content_type()); 26 | ASSERT_FALSE(decoded.has_key_id()); 27 | ASSERT_FALSE(decoded.has_issuer()); 28 | ASSERT_FALSE(decoded.has_subject()); 29 | ASSERT_TRUE(decoded.has_audience()); 30 | ASSERT_FALSE(decoded.has_expires_at()); 31 | ASSERT_FALSE(decoded.has_not_before()); 32 | ASSERT_FALSE(decoded.has_issued_at()); 33 | ASSERT_FALSE(decoded.has_id()); 34 | 35 | ASSERT_EQ("HS256", decoded.get_algorithm()); 36 | ASSERT_EQ("JWT", decoded.get_type()); 37 | auto aud = decoded.get_audience(); 38 | ASSERT_EQ(1, aud.size()); 39 | ASSERT_EQ("test", *aud.begin()); 40 | } 41 | 42 | TEST(BoostJsonTest, SetArray) { 43 | std::vector vect = {100, 20, 10}; 44 | auto token = jwt::create() 45 | .set_payload_claim("test", jwt::basic_claim(vect.begin(), vect.end())) 46 | .sign(jwt::algorithm::none{}); 47 | ASSERT_EQ(token, "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."); 48 | } 49 | 50 | TEST(BoostJsonTest, SetObject) { 51 | jwt::traits::boost_json::value_type value; 52 | ASSERT_TRUE(jwt::traits::boost_json::parse(value, "{\"api-x\": [1]}")); 53 | jwt::basic_claim object(value); 54 | ASSERT_EQ(object.get_type(), jwt::json::type::object); 55 | 56 | auto token = jwt::create() 57 | .set_payload_claim("namespace", object) 58 | .sign(jwt::algorithm::hs256("test")); 59 | ASSERT_EQ(token, 60 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"); 61 | } 62 | 63 | TEST(BoostJsonTest, VerifyTokenHS256) { 64 | jwt::traits::boost_json::string_type token = 65 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; 66 | 67 | const auto decoded_token = jwt::decode(token); 68 | const auto verify = 69 | jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); 70 | verify.verify(decoded_token); 71 | } 72 | 73 | TEST(BoostJsonTest, VerifyTokenExpirationValid) { 74 | const auto token = jwt::create() 75 | .set_issuer("auth0") 76 | .set_issued_at(std::chrono::system_clock::now()) 77 | .set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds{3600}) 78 | .sign(jwt::algorithm::hs256{"secret"}); 79 | 80 | const auto decoded_token = jwt::decode(token); 81 | const auto verify = 82 | jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); 83 | verify.verify(decoded_token); 84 | } 85 | 86 | TEST(BoostJsonTest, VerifyTokenExpirationInValid) { 87 | const auto token = jwt::create() 88 | .set_issuer("auth0") 89 | .set_issued_now() 90 | .set_expires_in(std::chrono::seconds{3600}) 91 | .sign(jwt::algorithm::hs256{"secret"}); 92 | 93 | const auto decoded_token = jwt::decode(token); 94 | const auto verify = 95 | jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); 96 | verify.verify(decoded_token); 97 | } 98 | 99 | TEST(BoostJsonTest, VerifyTokenExpired) { 100 | const auto token = jwt::create() 101 | .set_issuer("auth0") 102 | .set_issued_at(std::chrono::system_clock::now() - std::chrono::seconds{3601}) 103 | .set_expires_at(std::chrono::system_clock::now() - std::chrono::seconds{1}) 104 | .sign(jwt::algorithm::hs256{"secret"}); 105 | 106 | const auto decoded_token = jwt::decode(token); 107 | const auto verify = 108 | jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); 109 | ASSERT_THROW(verify.verify(decoded_token), jwt::error::token_verification_exception); 110 | 111 | std::error_code ec; 112 | ASSERT_NO_THROW(verify.verify(decoded_token, ec)); 113 | ASSERT_TRUE(!(!ec)); 114 | ASSERT_EQ(ec.category(), jwt::error::token_verification_error_category()); 115 | ASSERT_EQ(ec.value(), static_cast(jwt::error::token_verification_error::token_expired)); 116 | } 117 | 118 | TEST(BoostJsonTest, VerifyArray) { 119 | jwt::traits::boost_json::string_type token = "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."; 120 | const auto decoded_token = jwt::decode(token); 121 | 122 | std::vector vect = {100, 20, 10}; 123 | jwt::basic_claim array_claim(vect.begin(), vect.end()); 124 | const auto verify = 125 | jwt::verify().allow_algorithm(jwt::algorithm::none{}).with_claim("test", array_claim); 126 | ASSERT_NO_THROW(verify.verify(decoded_token)); 127 | } 128 | 129 | TEST(BoostJsonTest, VerifyObject) { 130 | jwt::traits::boost_json::string_type token = 131 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"; 132 | const auto decoded_token = jwt::decode(token); 133 | 134 | jwt::traits::boost_json::value_type value; 135 | ASSERT_TRUE(jwt::traits::boost_json::parse(value, "{\"api-x\": [1]}")); 136 | jwt::basic_claim object_claim(value); 137 | const auto verify = jwt::verify() 138 | .allow_algorithm(jwt::algorithm::hs256("test")) 139 | .with_claim("namespace", object_claim); 140 | ASSERT_NO_THROW(verify.verify(decoded_token)); 141 | } 142 | -------------------------------------------------------------------------------- /tests/traits/JsonconsTest.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/danielaparker-jsoncons/traits.h" 2 | 3 | #include 4 | 5 | TEST(JsonconsTest, BasicClaims) { 6 | const auto string = jwt::basic_claim( 7 | jwt::traits::danielaparker_jsoncons::string_type("string")); 8 | ASSERT_EQ(string.get_type(), jwt::json::type::string); 9 | 10 | const auto array = jwt::basic_claim( 11 | std::set{"string", "string"}); 12 | ASSERT_EQ(array.get_type(), jwt::json::type::array); 13 | 14 | const auto integer = jwt::basic_claim(159816816); 15 | ASSERT_EQ(integer.get_type(), jwt::json::type::integer); 16 | } 17 | 18 | TEST(JsonconsTest, AudienceAsString) { 19 | jwt::traits::danielaparker_jsoncons::string_type token = 20 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0In0.WZnM3SIiSRHsbO3O7Z2bmIzTJ4EC32HRBKfLznHhrh4"; 21 | auto decoded = jwt::decode(token); 22 | 23 | ASSERT_TRUE(decoded.has_algorithm()); 24 | ASSERT_TRUE(decoded.has_type()); 25 | ASSERT_FALSE(decoded.has_content_type()); 26 | ASSERT_FALSE(decoded.has_key_id()); 27 | ASSERT_FALSE(decoded.has_issuer()); 28 | ASSERT_FALSE(decoded.has_subject()); 29 | ASSERT_TRUE(decoded.has_audience()); 30 | ASSERT_FALSE(decoded.has_expires_at()); 31 | ASSERT_FALSE(decoded.has_not_before()); 32 | ASSERT_FALSE(decoded.has_issued_at()); 33 | ASSERT_FALSE(decoded.has_id()); 34 | 35 | ASSERT_EQ("HS256", decoded.get_algorithm()); 36 | ASSERT_EQ("JWT", decoded.get_type()); 37 | auto aud = decoded.get_audience(); 38 | ASSERT_EQ(1, aud.size()); 39 | ASSERT_EQ("test", *aud.begin()); 40 | } 41 | 42 | TEST(JsonconsTest, SetArray) { 43 | std::vector vect = {100, 20, 10}; 44 | auto token = 45 | jwt::create() 46 | .set_payload_claim("test", jwt::basic_claim(vect.begin(), vect.end())) 47 | .sign(jwt::algorithm::none{}); 48 | ASSERT_EQ(token, "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."); 49 | } 50 | 51 | TEST(JsonconsTest, SetObject) { 52 | std::istringstream iss{"{\"api-x\": [1]}"}; 53 | jwt::basic_claim object; 54 | iss >> object; 55 | ASSERT_EQ(object.get_type(), jwt::json::type::object); 56 | 57 | auto token = jwt::create() 58 | .set_payload_claim("namespace", object) 59 | .sign(jwt::algorithm::hs256("test")); 60 | ASSERT_EQ(token, 61 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"); 62 | } 63 | 64 | TEST(JsonconsTest, VerifyTokenHS256) { 65 | jwt::traits::danielaparker_jsoncons::string_type token = 66 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; 67 | 68 | const auto decoded_token = jwt::decode(token); 69 | const auto verify = jwt::verify() 70 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 71 | .with_issuer("auth0"); 72 | verify.verify(decoded_token); 73 | } 74 | 75 | TEST(JsonconsTest, VerifyTokenExpirationValid) { 76 | const auto token = jwt::create() 77 | .set_issuer("auth0") 78 | .set_issued_at(std::chrono::system_clock::now()) 79 | .set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds{3600}) 80 | .sign(jwt::algorithm::hs256{"secret"}); 81 | 82 | const auto decoded_token = jwt::decode(token); 83 | const auto verify = jwt::verify() 84 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 85 | .with_issuer("auth0"); 86 | verify.verify(decoded_token); 87 | } 88 | 89 | TEST(JsonconsTest, VerifyTokenExpirationInValid) { 90 | const auto token = jwt::create() 91 | .set_issuer("auth0") 92 | .set_issued_now() 93 | .set_expires_in(std::chrono::seconds{3600}) 94 | .sign(jwt::algorithm::hs256{"secret"}); 95 | 96 | const auto decoded_token = jwt::decode(token); 97 | const auto verify = jwt::verify() 98 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 99 | .with_issuer("auth0"); 100 | verify.verify(decoded_token); 101 | } 102 | 103 | TEST(JsonconsTest, VerifyTokenExpired) { 104 | const auto token = jwt::create() 105 | .set_issuer("auth0") 106 | .set_issued_at(std::chrono::system_clock::now() - std::chrono::seconds{3601}) 107 | .set_expires_at(std::chrono::system_clock::now() - std::chrono::seconds{1}) 108 | .sign(jwt::algorithm::hs256{"secret"}); 109 | 110 | const auto decoded_token = jwt::decode(token); 111 | const auto verify = jwt::verify() 112 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 113 | .with_issuer("auth0"); 114 | ASSERT_THROW(verify.verify(decoded_token), jwt::error::token_verification_exception); 115 | 116 | std::error_code ec; 117 | ASSERT_NO_THROW(verify.verify(decoded_token, ec)); 118 | ASSERT_TRUE(!(!ec)); 119 | ASSERT_EQ(ec.category(), jwt::error::token_verification_error_category()); 120 | ASSERT_EQ(ec.value(), static_cast(jwt::error::token_verification_error::token_expired)); 121 | } 122 | 123 | TEST(JsonconsTest, VerifyArray) { 124 | jwt::traits::danielaparker_jsoncons::string_type token = "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."; 125 | const auto decoded_token = jwt::decode(token); 126 | 127 | std::vector vect = {100, 20, 10}; 128 | jwt::basic_claim array_claim(vect.begin(), vect.end()); 129 | const auto verify = jwt::verify() 130 | .allow_algorithm(jwt::algorithm::none{}) 131 | .with_claim("test", array_claim); 132 | ASSERT_NO_THROW(verify.verify(decoded_token)); 133 | } 134 | 135 | TEST(JsonconsTest, VerifyObject) { 136 | jwt::traits::danielaparker_jsoncons::string_type token = 137 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"; 138 | const auto decoded_token = jwt::decode(token); 139 | 140 | jwt::basic_claim object_claim; 141 | std::istringstream iss{"{\"api-x\": [1]}"}; 142 | iss >> object_claim; 143 | const auto verify = jwt::verify() 144 | .allow_algorithm(jwt::algorithm::hs256("test")) 145 | .with_claim("namespace", object_claim); 146 | ASSERT_NO_THROW(verify.verify(decoded_token)); 147 | } 148 | -------------------------------------------------------------------------------- /tests/traits/NlohmannTest.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/nlohmann-json/traits.h" 2 | 3 | #include 4 | 5 | TEST(NlohmannTest, BasicClaims) { 6 | const auto string = jwt::basic_claim(jwt::traits::nlohmann_json::string_type("string")); 7 | ASSERT_EQ(string.get_type(), jwt::json::type::string); 8 | 9 | const auto array = jwt::basic_claim( 10 | std::set{"string", "string"}); 11 | ASSERT_EQ(array.get_type(), jwt::json::type::array); 12 | 13 | const auto integer = jwt::basic_claim(159816816); 14 | ASSERT_EQ(integer.get_type(), jwt::json::type::integer); 15 | } 16 | 17 | TEST(NlohmannTest, AudienceAsString) { 18 | jwt::traits::nlohmann_json::string_type token = 19 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0In0.WZnM3SIiSRHsbO3O7Z2bmIzTJ4EC32HRBKfLznHhrh4"; 20 | auto decoded = jwt::decode(token); 21 | 22 | ASSERT_TRUE(decoded.has_algorithm()); 23 | ASSERT_TRUE(decoded.has_type()); 24 | ASSERT_FALSE(decoded.has_content_type()); 25 | ASSERT_FALSE(decoded.has_key_id()); 26 | ASSERT_FALSE(decoded.has_issuer()); 27 | ASSERT_FALSE(decoded.has_subject()); 28 | ASSERT_TRUE(decoded.has_audience()); 29 | ASSERT_FALSE(decoded.has_expires_at()); 30 | ASSERT_FALSE(decoded.has_not_before()); 31 | ASSERT_FALSE(decoded.has_issued_at()); 32 | ASSERT_FALSE(decoded.has_id()); 33 | 34 | ASSERT_EQ("HS256", decoded.get_algorithm()); 35 | ASSERT_EQ("JWT", decoded.get_type()); 36 | auto aud = decoded.get_audience(); 37 | ASSERT_EQ(1, aud.size()); 38 | ASSERT_EQ("test", *aud.begin()); 39 | } 40 | 41 | TEST(NlohmannTest, SetArray) { 42 | std::vector vect = {100, 20, 10}; 43 | auto token = jwt::create() 44 | .set_payload_claim("test", jwt::basic_claim(vect.begin(), vect.end())) 45 | .sign(jwt::algorithm::none{}); 46 | ASSERT_EQ(token, "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."); 47 | } 48 | 49 | TEST(NlohmannTest, SetObject) { 50 | std::istringstream iss{"{\"api-x\": [1]}"}; 51 | jwt::basic_claim object; 52 | iss >> object; 53 | ASSERT_EQ(object.get_type(), jwt::json::type::object); 54 | 55 | auto token = jwt::create() 56 | .set_payload_claim("namespace", object) 57 | .sign(jwt::algorithm::hs256("test")); 58 | ASSERT_EQ(token, 59 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"); 60 | } 61 | 62 | TEST(NlohmannTest, VerifyTokenHS256) { 63 | jwt::traits::nlohmann_json::string_type token = 64 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; 65 | 66 | const auto decoded_token = jwt::decode(token); 67 | const auto verify = 68 | jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); 69 | verify.verify(decoded_token); 70 | } 71 | 72 | TEST(NlohmannTest, VerifyTokenExpirationValid) { 73 | const auto token = jwt::create() 74 | .set_issuer("auth0") 75 | .set_issued_at(std::chrono::system_clock::now()) 76 | .set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds{3600}) 77 | .sign(jwt::algorithm::hs256{"secret"}); 78 | 79 | const auto decoded_token = jwt::decode(token); 80 | const auto verify = 81 | jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); 82 | verify.verify(decoded_token); 83 | } 84 | 85 | TEST(NlohmannTest, VerifyTokenExpirationInValid) { 86 | const auto token = jwt::create() 87 | .set_issuer("auth0") 88 | .set_issued_now() 89 | .set_expires_in(std::chrono::seconds{3600}) 90 | .sign(jwt::algorithm::hs256{"secret"}); 91 | 92 | const auto decoded_token = jwt::decode(token); 93 | const auto verify = 94 | jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); 95 | verify.verify(decoded_token); 96 | } 97 | 98 | TEST(NlohmannTest, VerifyTokenExpired) { 99 | const auto token = jwt::create() 100 | .set_issuer("auth0") 101 | .set_issued_at(std::chrono::system_clock::now() - std::chrono::seconds{3601}) 102 | .set_expires_at(std::chrono::system_clock::now() - std::chrono::seconds{1}) 103 | .sign(jwt::algorithm::hs256{"secret"}); 104 | 105 | const auto decoded_token = jwt::decode(token); 106 | const auto verify = 107 | jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); 108 | ASSERT_THROW(verify.verify(decoded_token), jwt::error::token_verification_exception); 109 | 110 | std::error_code ec; 111 | ASSERT_NO_THROW(verify.verify(decoded_token, ec)); 112 | ASSERT_TRUE(!(!ec)); 113 | ASSERT_EQ(ec.category(), jwt::error::token_verification_error_category()); 114 | ASSERT_EQ(ec.value(), static_cast(jwt::error::token_verification_error::token_expired)); 115 | } 116 | 117 | TEST(NlohmannTest, VerifyArray) { 118 | jwt::traits::nlohmann_json::string_type token = "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."; 119 | const auto decoded_token = jwt::decode(token); 120 | 121 | std::vector vect = {100, 20, 10}; 122 | jwt::basic_claim array_claim(vect.begin(), vect.end()); 123 | const auto verify = jwt::verify() 124 | .allow_algorithm(jwt::algorithm::none{}) 125 | .with_claim("test", array_claim); 126 | ASSERT_NO_THROW(verify.verify(decoded_token)); 127 | } 128 | 129 | TEST(NlohmannTest, VerifyObject) { 130 | jwt::traits::nlohmann_json::string_type token = 131 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"; 132 | const auto decoded_token = jwt::decode(token); 133 | 134 | jwt::basic_claim object_claim; 135 | std::istringstream iss{"{\"api-x\": [1]}"}; 136 | iss >> object_claim; 137 | const auto verify = jwt::verify() 138 | .allow_algorithm(jwt::algorithm::hs256("test")) 139 | .with_claim("namespace", object_claim); 140 | ASSERT_NO_THROW(verify.verify(decoded_token)); 141 | } 142 | -------------------------------------------------------------------------------- /tests/traits/OspJsoncppTest.cpp: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h" 2 | 3 | #include 4 | 5 | TEST(OspJsoncppTest, BasicClaims) { 6 | const auto string = jwt::basic_claim( 7 | jwt::traits::open_source_parsers_jsoncpp::string_type("string")); 8 | ASSERT_EQ(string.get_type(), jwt::json::type::string); 9 | 10 | const auto array = jwt::basic_claim( 11 | std::set{"string", "string"}); 12 | ASSERT_EQ(array.get_type(), jwt::json::type::array); 13 | 14 | const auto integer = jwt::basic_claim(159816816); 15 | ASSERT_EQ(integer.get_type(), jwt::json::type::integer); 16 | } 17 | 18 | TEST(OspJsoncppTest, AudienceAsString) { 19 | jwt::traits::open_source_parsers_jsoncpp::string_type token = 20 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0In0.WZnM3SIiSRHsbO3O7Z2bmIzTJ4EC32HRBKfLznHhrh4"; 21 | auto decoded = jwt::decode(token); 22 | 23 | ASSERT_TRUE(decoded.has_algorithm()); 24 | ASSERT_TRUE(decoded.has_type()); 25 | ASSERT_FALSE(decoded.has_content_type()); 26 | ASSERT_FALSE(decoded.has_key_id()); 27 | ASSERT_FALSE(decoded.has_issuer()); 28 | ASSERT_FALSE(decoded.has_subject()); 29 | ASSERT_TRUE(decoded.has_audience()); 30 | ASSERT_FALSE(decoded.has_expires_at()); 31 | ASSERT_FALSE(decoded.has_not_before()); 32 | ASSERT_FALSE(decoded.has_issued_at()); 33 | ASSERT_FALSE(decoded.has_id()); 34 | 35 | ASSERT_EQ("HS256", decoded.get_algorithm()); 36 | ASSERT_EQ("JWT", decoded.get_type()); 37 | auto aud = decoded.get_audience(); 38 | ASSERT_EQ(1, aud.size()); 39 | ASSERT_EQ("test", *aud.begin()); 40 | } 41 | 42 | TEST(OspJsoncppTest, SetArray) { 43 | std::vector vect = {100, 20, 10}; 44 | auto token = jwt::create() 45 | .set_payload_claim( 46 | "test", jwt::basic_claim(vect.begin(), vect.end())) 47 | .sign(jwt::algorithm::none{}); 48 | ASSERT_EQ(token, "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."); 49 | } 50 | 51 | TEST(OspJsoncppTest, SetObject) { 52 | std::istringstream iss{"{\"api-x\": [1]}"}; 53 | jwt::basic_claim object; 54 | iss >> object; 55 | ASSERT_EQ(object.get_type(), jwt::json::type::object); 56 | 57 | auto token = jwt::create() 58 | .set_payload_claim("namespace", object) 59 | .sign(jwt::algorithm::hs256("test")); 60 | ASSERT_EQ(token, 61 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"); 62 | } 63 | 64 | TEST(OspJsoncppTest, VerifyTokenHS256) { 65 | jwt::traits::open_source_parsers_jsoncpp::string_type token = 66 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; 67 | 68 | const auto decoded_token = jwt::decode(token); 69 | const auto verify = jwt::verify() 70 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 71 | .with_issuer("auth0"); 72 | verify.verify(decoded_token); 73 | } 74 | 75 | TEST(OspJsoncppTest, VerifyTokenExpirationValid) { 76 | const auto token = jwt::create() 77 | .set_issuer("auth0") 78 | .set_issued_at(std::chrono::system_clock::now()) 79 | .set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds{3600}) 80 | .sign(jwt::algorithm::hs256{"secret"}); 81 | 82 | const auto decoded_token = jwt::decode(token); 83 | const auto verify = jwt::verify() 84 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 85 | .with_issuer("auth0"); 86 | verify.verify(decoded_token); 87 | } 88 | 89 | TEST(OspJsoncppTest, VerifyTokenExpirationInValid) { 90 | const auto token = jwt::create() 91 | .set_issuer("auth0") 92 | .set_issued_now() 93 | .set_expires_in(std::chrono::seconds{3600}) 94 | .sign(jwt::algorithm::hs256{"secret"}); 95 | 96 | const auto decoded_token = jwt::decode(token); 97 | const auto verify = jwt::verify() 98 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 99 | .with_issuer("auth0"); 100 | verify.verify(decoded_token); 101 | } 102 | 103 | TEST(OspJsoncppTest, VerifyTokenExpired) { 104 | const auto token = jwt::create() 105 | .set_issuer("auth0") 106 | .set_issued_at(std::chrono::system_clock::now() - std::chrono::seconds{3601}) 107 | .set_expires_at(std::chrono::system_clock::now() - std::chrono::seconds{1}) 108 | .sign(jwt::algorithm::hs256{"secret"}); 109 | 110 | const auto decoded_token = jwt::decode(token); 111 | const auto verify = jwt::verify() 112 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 113 | .with_issuer("auth0"); 114 | ASSERT_THROW(verify.verify(decoded_token), jwt::error::token_verification_exception); 115 | 116 | std::error_code ec; 117 | ASSERT_NO_THROW(verify.verify(decoded_token, ec)); 118 | ASSERT_TRUE(!(!ec)); 119 | ASSERT_EQ(ec.category(), jwt::error::token_verification_error_category()); 120 | ASSERT_EQ(ec.value(), static_cast(jwt::error::token_verification_error::token_expired)); 121 | } 122 | 123 | TEST(OspJsoncppTest, VerifyArray) { 124 | jwt::traits::open_source_parsers_jsoncpp::string_type token = "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."; 125 | const auto decoded_token = jwt::decode(token); 126 | 127 | std::vector vect = {100, 20, 10}; 128 | jwt::basic_claim array_claim(vect.begin(), vect.end()); 129 | const auto verify = jwt::verify() 130 | .allow_algorithm(jwt::algorithm::none{}) 131 | .with_claim("test", array_claim); 132 | ASSERT_NO_THROW(verify.verify(decoded_token)); 133 | } 134 | 135 | TEST(OspJsoncppTest, VerifyObject) { 136 | jwt::traits::open_source_parsers_jsoncpp::string_type token = 137 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"; 138 | const auto decoded_token = jwt::decode(token); 139 | 140 | jwt::basic_claim object_claim; 141 | std::istringstream iss{"{\"api-x\": [1]}"}; 142 | iss >> object_claim; 143 | const auto verify = jwt::verify() 144 | .allow_algorithm(jwt::algorithm::hs256("test")) 145 | .with_claim("namespace", object_claim); 146 | ASSERT_NO_THROW(verify.verify(decoded_token)); 147 | } 148 | -------------------------------------------------------------------------------- /tests/traits/TraitsTest.cpp.mustache: -------------------------------------------------------------------------------- 1 | #include "jwt-cpp/traits/{{traits_dir}}/traits.h" 2 | 3 | #include 4 | 5 | TEST({{test_suite_name}}, BasicClaims) { 6 | const auto string = jwt::basic_claim( 7 | jwt::traits::{{traits_name}}::string_type("string")); 8 | ASSERT_EQ(string.get_type(), jwt::json::type::string); 9 | 10 | const auto array = jwt::basic_claim( 11 | std::set{"string", "string"}); 12 | ASSERT_EQ(array.get_type(), jwt::json::type::array); 13 | 14 | const auto integer = jwt::basic_claim(159816816); 15 | ASSERT_EQ(integer.get_type(), jwt::json::type::integer); 16 | } 17 | 18 | TEST({{test_suite_name}}, AudienceAsString) { 19 | jwt::traits::{{traits_name}}::string_type token = 20 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0In0.WZnM3SIiSRHsbO3O7Z2bmIzTJ4EC32HRBKfLznHhrh4"; 21 | auto decoded = jwt::decode(token); 22 | 23 | ASSERT_TRUE(decoded.has_algorithm()); 24 | ASSERT_TRUE(decoded.has_type()); 25 | ASSERT_FALSE(decoded.has_content_type()); 26 | ASSERT_FALSE(decoded.has_key_id()); 27 | ASSERT_FALSE(decoded.has_issuer()); 28 | ASSERT_FALSE(decoded.has_subject()); 29 | ASSERT_TRUE(decoded.has_audience()); 30 | ASSERT_FALSE(decoded.has_expires_at()); 31 | ASSERT_FALSE(decoded.has_not_before()); 32 | ASSERT_FALSE(decoded.has_issued_at()); 33 | ASSERT_FALSE(decoded.has_id()); 34 | 35 | ASSERT_EQ("HS256", decoded.get_algorithm()); 36 | ASSERT_EQ("JWT", decoded.get_type()); 37 | auto aud = decoded.get_audience(); 38 | ASSERT_EQ(1, aud.size()); 39 | ASSERT_EQ("test", *aud.begin()); 40 | } 41 | 42 | TEST({{test_suite_name}}, SetArray) { 43 | std::vector vect = {100, 20, 10}; 44 | auto token = 45 | jwt::create() 46 | .set_payload_claim("test", jwt::basic_claim(vect.begin(), vect.end())) 47 | .sign(jwt::algorithm::none{}); 48 | ASSERT_EQ(token, "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."); 49 | } 50 | 51 | TEST({{test_suite_name}}, SetObject) { 52 | std::istringstream iss{"{\"api-x\": [1]}"}; 53 | jwt::basic_claim object; 54 | iss >> object; 55 | ASSERT_EQ(object.get_type(), jwt::json::type::object); 56 | 57 | auto token = jwt::create() 58 | .set_payload_claim("namespace", object) 59 | .sign(jwt::algorithm::hs256("test")); 60 | ASSERT_EQ(token, 61 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"); 62 | } 63 | 64 | TEST({{test_suite_name}}, VerifyTokenHS256) { 65 | jwt::traits::{{traits_name}}::string_type token = 66 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; 67 | 68 | const auto decoded_token = jwt::decode(token); 69 | const auto verify = jwt::verify() 70 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 71 | .with_issuer("auth0"); 72 | verify.verify(decoded_token); 73 | } 74 | 75 | TEST({{test_suite_name}}, VerifyTokenExpirationValid) { 76 | const auto token = jwt::create() 77 | .set_issuer("auth0") 78 | .set_issued_at(std::chrono::system_clock::now()) 79 | .set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds{3600}) 80 | .sign(jwt::algorithm::hs256{"secret"}); 81 | 82 | const auto decoded_token = jwt::decode(token); 83 | const auto verify = jwt::verify() 84 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 85 | .with_issuer("auth0"); 86 | verify.verify(decoded_token); 87 | } 88 | 89 | TEST({{test_suite_name}}, VerifyTokenExpirationInValid) { 90 | const auto token = jwt::create() 91 | .set_issuer("auth0") 92 | .set_issued_now() 93 | .set_expires_in(std::chrono::seconds{3600}) 94 | .sign(jwt::algorithm::hs256{"secret"}); 95 | 96 | const auto decoded_token = jwt::decode(token); 97 | const auto verify = jwt::verify() 98 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 99 | .with_issuer("auth0"); 100 | verify.verify(decoded_token); 101 | } 102 | 103 | TEST({{test_suite_name}}, VerifyTokenExpired) { 104 | const auto token = jwt::create() 105 | .set_issuer("auth0") 106 | .set_issued_at(std::chrono::system_clock::now() - std::chrono::seconds{3601}) 107 | .set_expires_at(std::chrono::system_clock::now() - std::chrono::seconds{1}) 108 | .sign(jwt::algorithm::hs256{"secret"}); 109 | 110 | const auto decoded_token = jwt::decode(token); 111 | const auto verify = jwt::verify() 112 | .allow_algorithm(jwt::algorithm::hs256{"secret"}) 113 | .with_issuer("auth0"); 114 | ASSERT_THROW(verify.verify(decoded_token), jwt::error::token_verification_exception); 115 | 116 | std::error_code ec; 117 | ASSERT_NO_THROW(verify.verify(decoded_token, ec)); 118 | ASSERT_TRUE(!(!ec)); 119 | ASSERT_EQ(ec.category(), jwt::error::token_verification_error_category()); 120 | ASSERT_EQ(ec.value(), static_cast(jwt::error::token_verification_error::token_expired)); 121 | } 122 | 123 | TEST({{test_suite_name}}, VerifyArray) { 124 | jwt::traits::{{traits_name}}::string_type token = "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."; 125 | const auto decoded_token = jwt::decode(token); 126 | 127 | std::vector vect = {100, 20, 10}; 128 | jwt::basic_claim array_claim(vect.begin(), vect.end()); 129 | const auto verify = jwt::verify() 130 | .allow_algorithm(jwt::algorithm::none{}) 131 | .with_claim("test", array_claim); 132 | ASSERT_NO_THROW(verify.verify(decoded_token)); 133 | } 134 | 135 | TEST({{test_suite_name}}, VerifyObject) { 136 | jwt::traits::{{traits_name}}::string_type token = 137 | "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"; 138 | const auto decoded_token = jwt::decode(token); 139 | 140 | jwt::basic_claim object_claim; 141 | std::istringstream iss{"{\"api-x\": [1]}"}; 142 | iss >> object_claim; 143 | const auto verify = jwt::verify() 144 | .allow_algorithm(jwt::algorithm::hs256("test")) 145 | .with_claim("namespace", object_claim); 146 | ASSERT_NO_THROW(verify.verify(decoded_token)); 147 | } 148 | --------------------------------------------------------------------------------