├── .editorconfig ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .ocamlformat ├── CHANGES.md ├── Justfile ├── LICENSE ├── README.md ├── docs ├── highlight.pack.js ├── index.html ├── jwto │ ├── Jwto │ │ ├── .dune-keep │ │ └── index.html │ └── index.html └── odoc.css ├── dune-project ├── jwto.opam ├── lib ├── dune ├── jwto.ml └── jwto.mli └── test ├── dune └── test_jwto.ml /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.ml] 2 | indent_style = space 3 | indent_size = 2 4 | 5 | [*.md] 6 | indent_style = space 7 | indent_size = 2 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: 15 | - macos-latest 16 | - ubuntu-latest 17 | - windows-latest 18 | 19 | ocaml-compiler: 20 | - 4.08.x 21 | - 4.09.x 22 | - 4.10.x 23 | - 4.11.x 24 | - 4.12.x 25 | 26 | runs-on: ${{ matrix.os }} 27 | 28 | steps: 29 | - name: Checkout code 30 | uses: actions/checkout@v2 31 | 32 | - name: Use OCaml ${{ matrix.ocaml-compiler }} 33 | uses: ocaml/setup-ocaml@v2 34 | with: 35 | ocaml-compiler: ${{ matrix.ocaml-compiler }} 36 | 37 | - run: opam install . --deps-only --with-test 38 | 39 | - run: opam exec -- dune build @all 40 | 41 | - name: Tests 42 | run: | 43 | opam exec -- dune build @runtest 44 | 45 | - name: Opam Lint 46 | run: | 47 | opam lint jwto.opam 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _opam 3 | .merlin 4 | *.install 5 | *.docdir 6 | *.native 7 | *.byte 8 | *.swp 9 | 10 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | profile=sparse 2 | dock-collection-brackets=true 3 | break-separators=after 4 | function-indent=4 5 | if-then-else=k-r 6 | let-binding-indent=4 7 | margin=64 -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 0.4.0 2 | 3 | - Use `digestif` instead of `cryptokit` 4 | 5 | ## 0.3.0 6 | 7 | - Added support for `Bool` in claim type 8 | 9 | ## 0.2.2 10 | 11 | - Expose getters `get_header`, `get_payload` and `get_signature` 12 | 13 | ## 0.2.1 14 | 15 | - Fix `is_valid` when the payload has more than one attribute 16 | 17 | ## 0.2 18 | 19 | - Use Base63 v3 -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | all: 2 | opam exec -- dune build --root . @install 3 | 4 | test: 5 | opam exec -- dune runtest --root . 6 | 7 | release: all 8 | opam exec -- dune-release tag 9 | opam exec -- dune-release distrib 10 | opam exec -- dune-release publish distrib -y 11 | opam exec -- dune-release opam pkg 12 | opam exec -- dune-release opam submit --no-auto-open -y 13 | 14 | # Generate odoc documentation 15 | doc: 16 | opam exec -- dune build --root . @doc 17 | rm -rf ./docs 18 | cp -r _build/default/_doc/_html ./docs 19 | 20 | # Open odoc documentation with default web browser 21 | servedoc: doc 22 | open _build/default/_doc/_html/index.html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Sebastian Porto 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ocaml JWT 2 | 3 | ## Create and encode a signed token 4 | 5 | A payload is a list of tuples `(string, string)`: 6 | 7 | ```ocaml 8 | let payload = 9 | [ 10 | ("user", "sam"); 11 | ("age", "17"); 12 | ] 13 | ``` 14 | 15 | For the signature algorithm, `Jwto` supports HMAC applied to SHA-256 or SHA-512. 16 | 17 | We can sign and encode the payload in one go by doing: 18 | 19 | ```ocaml 20 | Jwto.encode Jwto.HS256 "secret" payload 21 | 22 | --> 23 | 24 | "eyJhbGciOiJIUzI1NiJ9..." 25 | ``` 26 | 27 | ## Decode token 28 | 29 | To decode the token without verifying it, and get a `Jwto.t`: 30 | 31 | ```ocaml 32 | let signed_token = 33 | match Jwto.decode "eyJhbGciOiJIUzI1NiJ9..." with 34 | | Ok t -> t 35 | | Error err -> failwith err 36 | 37 | --> 38 | 39 | { header = ...; payload = [...]; signature = ... } 40 | ``` 41 | 42 | ## Verify token 43 | 44 | ```ocaml 45 | Jwto.is_valid "secret" signed_token 46 | 47 | --> 48 | 49 | true 50 | ``` 51 | 52 | ## Decode and verify 53 | 54 | To decode and verify the token in one go: 55 | 56 | ```ocaml 57 | Jwto.decode_and_verify "secret" "eyJhbGciOiJIUzI1NiJ9..." 58 | 59 | --> 60 | 61 | Ok { header = ...; payload = [...]; signature = ... } 62 | ``` 63 | 64 | If the verification fails, you will get an `Error`. 65 | -------------------------------------------------------------------------------- /docs/highlight.pack.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.15.8 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(a){var f=[],u=Object.keys,N={},c={},n=/^(no-?highlight|plain|text)$/i,s=/\blang(?:uage)?-([\w-]+)\b/i,t=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,r={case_insensitive:"cI",lexemes:"l",contains:"c",keywords:"k",subLanguage:"sL",className:"cN",begin:"b",beginKeywords:"bK",end:"e",endsWithParent:"eW",illegal:"i",excludeBegin:"eB",excludeEnd:"eE",returnBegin:"rB",returnEnd:"rE",relevance:"r",variants:"v",IDENT_RE:"IR",UNDERSCORE_IDENT_RE:"UIR",NUMBER_RE:"NR",C_NUMBER_RE:"CNR",BINARY_NUMBER_RE:"BNR",RE_STARTERS_RE:"RSR",BACKSLASH_ESCAPE:"BE",APOS_STRING_MODE:"ASM",QUOTE_STRING_MODE:"QSM",PHRASAL_WORDS_MODE:"PWM",C_LINE_COMMENT_MODE:"CLCM",C_BLOCK_COMMENT_MODE:"CBCM",HASH_COMMENT_MODE:"HCM",NUMBER_MODE:"NM",C_NUMBER_MODE:"CNM",BINARY_NUMBER_MODE:"BNM",CSS_NUMBER_MODE:"CSSNM",REGEXP_MODE:"RM",TITLE_MODE:"TM",UNDERSCORE_TITLE_MODE:"UTM",COMMENT:"C",beginRe:"bR",endRe:"eR",illegalRe:"iR",lexemesRe:"lR",terminators:"t",terminator_end:"tE"},b="",h={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};function _(e){return e.replace(/&/g,"&").replace(//g,">")}function E(e){return e.nodeName.toLowerCase()}function v(e,n){var t=e&&e.exec(n);return t&&0===t.index}function l(e){return n.test(e)}function g(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function R(e){var a=[];return function e(n,t){for(var r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?t+=r.nodeValue.length:1===r.nodeType&&(a.push({event:"start",offset:t,node:r}),t=e(r,t),E(r).match(/br|hr|img|input/)||a.push({event:"stop",offset:t,node:r}));return t}(e,0),a}function i(e){if(r&&!e.langApiRestored){for(var n in e.langApiRestored=!0,r)e[n]&&(e[r[n]]=e[n]);(e.c||[]).concat(e.v||[]).forEach(i)}}function m(o){function s(e){return e&&e.source||e}function c(e,n){return new RegExp(s(e),"m"+(o.cI?"i":"")+(n?"g":""))}!function n(t,e){if(!t.compiled){if(t.compiled=!0,t.k=t.k||t.bK,t.k){function r(t,e){o.cI&&(e=e.toLowerCase()),e.split(" ").forEach(function(e){var n=e.split("|");a[n[0]]=[t,n[1]?Number(n[1]):1]})}var a={};"string"==typeof t.k?r("keyword",t.k):u(t.k).forEach(function(e){r(e,t.k[e])}),t.k=a}t.lR=c(t.l||/\w+/,!0),e&&(t.bK&&(t.b="\\b("+t.bK.split(" ").join("|")+")\\b"),t.b||(t.b=/\B|\b/),t.bR=c(t.b),t.endSameAsBegin&&(t.e=t.b),t.e||t.eW||(t.e=/\B|\b/),t.e&&(t.eR=c(t.e)),t.tE=s(t.e)||"",t.eW&&e.tE&&(t.tE+=(t.e?"|":"")+e.tE)),t.i&&(t.iR=c(t.i)),null==t.r&&(t.r=1),t.c||(t.c=[]),t.c=Array.prototype.concat.apply([],t.c.map(function(e){return function(n){return n.v&&!n.cached_variants&&(n.cached_variants=n.v.map(function(e){return g(n,{v:null},e)})),n.cached_variants||n.eW&&[g(n)]||[n]}("self"===e?t:e)})),t.c.forEach(function(e){n(e,t)}),t.starts&&n(t.starts,e);var i=t.c.map(function(e){return e.bK?"\\.?(?:"+e.b+")\\.?":e.b}).concat([t.tE,t.i]).map(s).filter(Boolean);t.t=i.length?c(function(e,n){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i')+n+(t?"":b):n}function o(){E+=null!=l.sL?function(){var e="string"==typeof l.sL;if(e&&!N[l.sL])return _(g);var n=e?C(l.sL,g,!0,f[l.sL]):O(g,l.sL.length?l.sL:void 0);return 0")+'"');return g+=n,n.length||1}var s=B(e);if(!s)throw new Error('Unknown language: "'+e+'"');m(s);var a,l=t||s,f={},E="";for(a=l;a!==s;a=a.parent)a.cN&&(E=c(a.cN,"",!0)+E);var g="",R=0;try{for(var d,p,M=0;l.t.lastIndex=M,d=l.t.exec(n);)p=r(n.substring(M,d.index),d[0]),M=d.index+p;for(r(n.substr(M)),a=l;a.parent;a=a.parent)a.cN&&(E+=b);return{r:R,value:E,language:e,top:l}}catch(e){if(e.message&&-1!==e.message.indexOf("Illegal"))return{r:0,value:_(n)};throw e}}function O(t,e){e=e||h.languages||u(N);var r={r:0,value:_(t)},a=r;return e.filter(B).filter(M).forEach(function(e){var n=C(e,t,!1);n.language=e,n.r>a.r&&(a=n),n.r>r.r&&(a=r,r=n)}),a.language&&(r.second_best=a),r}function d(e){return h.tabReplace||h.useBR?e.replace(t,function(e,n){return h.useBR&&"\n"===e?"
":h.tabReplace?n.replace(/\t/g,h.tabReplace):""}):e}function o(e){var n,t,r,a,i,o=function(e){var n,t,r,a,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=s.exec(i))return B(t[1])?t[1]:"no-highlight";for(n=0,r=(i=i.split(/\s+/)).length;n/g,"\n"):n=e,i=n.textContent,r=o?C(o,i,!0):O(i),(t=R(n)).length&&((a=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=r.value,r.value=function(e,n,t){var r=0,a="",i=[];function o(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){a+=""}function s(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var l=o();if(a+=_(t.substring(r,l[0].offset)),r=l[0].offset,l===e){for(i.reverse().forEach(u);s(l.splice(0,1)[0]),(l=o())===e&&l.length&&l[0].offset===r;);i.reverse().forEach(c)}else"start"===l[0].event?i.push(l[0].node):i.pop(),s(l.splice(0,1)[0])}return a+_(t.substr(r))}(t,R(a),i)),r.value=d(r.value),e.innerHTML=r.value,e.className=function(e,n,t){var r=n?c[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}(e.className,o,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function p(){if(!p.called){p.called=!0;var e=document.querySelectorAll("pre code");f.forEach.call(e,o)}}function B(e){return e=(e||"").toLowerCase(),N[e]||N[c[e]]}function M(e){var n=B(e);return n&&!n.disableAutodetect}return a.highlight=C,a.highlightAuto=O,a.fixMarkup=d,a.highlightBlock=o,a.configure=function(e){h=g(h,e)},a.initHighlighting=p,a.initHighlightingOnLoad=function(){addEventListener("DOMContentLoaded",p,!1),addEventListener("load",p,!1)},a.registerLanguage=function(n,e){var t=N[n]=e(a);i(t),t.aliases&&t.aliases.forEach(function(e){c[e]=n})},a.listLanguages=function(){return u(N)},a.getLanguage=B,a.autoDetection=M,a.inherit=g,a.IR=a.IDENT_RE="[a-zA-Z]\\w*",a.UIR=a.UNDERSCORE_IDENT_RE="[a-zA-Z_]\\w*",a.NR=a.NUMBER_RE="\\b\\d+(\\.\\d+)?",a.CNR=a.C_NUMBER_RE="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",a.BNR=a.BINARY_NUMBER_RE="\\b(0b[01]+)",a.RSR=a.RE_STARTERS_RE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",a.BE=a.BACKSLASH_ESCAPE={b:"\\\\[\\s\\S]",r:0},a.ASM=a.APOS_STRING_MODE={cN:"string",b:"'",e:"'",i:"\\n",c:[a.BE]},a.QSM=a.QUOTE_STRING_MODE={cN:"string",b:'"',e:'"',i:"\\n",c:[a.BE]},a.PWM=a.PHRASAL_WORDS_MODE={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},a.C=a.COMMENT=function(e,n,t){var r=a.inherit({cN:"comment",b:e,e:n,c:[]},t||{});return r.c.push(a.PWM),r.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),r},a.CLCM=a.C_LINE_COMMENT_MODE=a.C("//","$"),a.CBCM=a.C_BLOCK_COMMENT_MODE=a.C("/\\*","\\*/"),a.HCM=a.HASH_COMMENT_MODE=a.C("#","$"),a.NM=a.NUMBER_MODE={cN:"number",b:a.NR,r:0},a.CNM=a.C_NUMBER_MODE={cN:"number",b:a.CNR,r:0},a.BNM=a.BINARY_NUMBER_MODE={cN:"number",b:a.BNR,r:0},a.CSSNM=a.CSS_NUMBER_MODE={cN:"number",b:a.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},a.RM=a.REGEXP_MODE={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[a.BE,{b:/\[/,e:/\]/,r:0,c:[a.BE]}]},a.TM=a.TITLE_MODE={cN:"title",b:a.IR,r:0},a.UTM=a.UNDERSCORE_TITLE_MODE={cN:"title",b:a.UIR,r:0},a.METHOD_GUARD={b:"\\.\\s*"+a.UIR,r:0},a});hljs.registerLanguage("ocaml",function(e){return{aliases:["ml"],k:{keyword:"and as assert asr begin class constraint do done downto else end exception external for fun function functor if in include inherit! inherit initializer land lazy let lor lsl lsr lxor match method!|10 method mod module mutable new object of open! open or private rec sig struct then to try type val! val virtual when while with parser value",built_in:"array bool bytes char exn|5 float int int32 int64 list lazy_t|5 nativeint|5 string unit in_channel out_channel ref",literal:"true false"},i:/\/\/|>>/,l:"[a-z_]\\w*!?",c:[{cN:"literal",b:"\\[(\\|\\|)?\\]|\\(\\)",r:0},e.C("\\(\\*","\\*\\)",{c:["self"]}),{cN:"symbol",b:"'[A-Za-z_](?!')[\\w']*"},{cN:"type",b:"`[A-Z][\\w']*"},{cN:"type",b:"\\b[A-Z][\\w']*",r:0},{b:"[a-z_]\\w*'[\\w']*",r:0},e.inherit(e.ASM,{cN:"string",r:0}),e.inherit(e.QSM,{i:null}),{cN:"number",b:"\\b(0[xX][a-fA-F0-9_]+[Lln]?|0[oO][0-7_]+[Lln]?|0[bB][01_]+[Lln]?|[0-9][0-9_]*([Lln]|(\\.[0-9_]*)?([eE][-+]?[0-9_]+)?)?)",r:0},{b:/[-=]>/}]}});hljs.registerLanguage("reasonml",function(r){var e="~?[a-z$_][0-9a-zA-Z$_]*",a="`?[A-Z$_][0-9a-zA-Z$_]*",c="("+["||","&&","++","**","+.","*","/","*.","/.","...","|>"].map(function(r){return r.split("").map(function(r){return"\\"+r}).join("")}).join("|")+"|==|===)",n="\\s+"+c+"\\s+",t={keyword:"and as asr assert begin class constraint do done downto else end exception externalfor fun function functor if in include inherit initializerland lazy let lor lsl lsr lxor match method mod module mutable new nonrecobject of open or private rec sig struct then to try type val virtual when while with",built_in:"array bool bytes char exn|5 float int int32 int64 list lazy_t|5 nativeint|5 ref string unit ",literal:"true false"},i="\\b(0[xX][a-fA-F0-9_]+[Lln]?|0[oO][0-7_]+[Lln]?|0[bB][01_]+[Lln]?|[0-9][0-9_]*([Lln]|(\\.[0-9_]*)?([eE][-+]?[0-9_]+)?)?)",s={cN:"number",r:0,v:[{b:i},{b:"\\(\\-"+i+"\\)"}]},b={cN:"operator",r:0,b:c},o=[{cN:"identifier",r:0,b:e},b,s],l=[r.QSM,b,{cN:"module",b:"\\b"+a,rB:!0,e:".",c:[{cN:"identifier",b:a,r:0}]}],u=[{cN:"module",b:"\\b"+a,rB:!0,e:".",r:0,c:[{cN:"identifier",b:a,r:0}]}],_={cN:"function",r:0,k:t,v:[{b:"\\s(\\(\\.?.*?\\)|"+e+")\\s*=>",e:"\\s*=>",rB:!0,r:0,c:[{cN:"params",v:[{b:e},{b:"~?[a-z$_][0-9a-zA-Z$_]*(s*:s*[a-z$_][0-9a-z$_]*((s*('?[a-z$_][0-9a-z$_]*s*(,'?[a-z$_][0-9a-z$_]*)*)?s*))?)?(s*:s*[a-z$_][0-9a-z$_]*((s*('?[a-z$_][0-9a-z$_]*s*(,'?[a-z$_][0-9a-z$_]*)*)?s*))?)?"},{b:/\(\s*\)/}]}]},{b:"\\s\\(\\.?[^;\\|]*\\)\\s*=>",e:"\\s=>",rB:!0,r:0,c:[{cN:"params",r:0,v:[{b:e,e:"(,|\\n|\\))",r:0,c:[b,{cN:"typing",b:":",e:"(,|\\n)",rB:!0,r:0,c:u}]}]}]},{b:"\\(\\.\\s"+e+"\\)\\s*=>"}]};l.push(_);var N={cN:"constructor",b:a+"\\(",e:"\\)",i:"\\n",k:t,c:[r.QSM,b,{cN:"params",b:"\\b"+e}]},d={cN:"pattern-match",b:"\\|",rB:!0,k:t,e:"=>",r:0,c:[N,b,{r:0,cN:"constructor",b:a}]},z={cN:"module-access",k:t,rB:!0,v:[{b:"\\b("+a+"\\.)+"+e},{b:"\\b("+a+"\\.)+\\(",e:"\\)",rB:!0,c:[_,{b:"\\(",e:"\\)",skip:!0}].concat(l)},{b:"\\b("+a+"\\.)+{",e:"}"}],c:l};return u.push(z),{aliases:["re"],k:t,i:"(:\\-|:=|\\${|\\+=)",c:[r.C("/\\*","\\*/",{i:"^(\\#,\\/\\/)"}),{cN:"character",b:"'(\\\\[^']+|[^'])'",i:"\\n",r:0},r.QSM,{cN:"literal",b:"\\(\\)",r:0},{cN:"literal",b:"\\[\\|",e:"\\|\\]",r:0,c:o},{cN:"literal",b:"\\[",e:"\\]",r:0,c:o},N,{cN:"operator",b:n,i:"\\-\\->",r:0},s,r.CLCM,d,_,{cN:"module-def",b:"\\bmodule\\s+"+e+"\\s+"+a+"\\s+=\\s+{",e:"}",rB:!0,k:t,r:0,c:[{cN:"module",r:0,b:a},{b:"{",e:"}",skip:!0}].concat(l)},z]}}); -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | index 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |

OCaml package documentation

13 |
    14 |
  1. jwto
  2. 15 |
16 |
17 |
18 | 19 | -------------------------------------------------------------------------------- /docs/jwto/Jwto/.dune-keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sporto/jwto/05cb9cad0a73e50bd57f42cc766a49f3927f59c2/docs/jwto/Jwto/.dune-keep -------------------------------------------------------------------------------- /docs/jwto/Jwto/index.html: -------------------------------------------------------------------------------- 1 | 2 | Jwto (jwto.Jwto)

Module Jwto

type algorithm =
| HS256
| HS512
| Unknown
type t

The JWT type.

Which has a

  • header
  • payload
  • and signature
type header

A header has the algorithm used and the type of the token which is JWT

type payload = (string * string) list

The payload of the token is a list of tuples, where each tuple is a "claim"

type unsigned_token

A JWT that hasn't been signed yet

val encode : algorithm -> string -> payload -> (string, string) Stdlib.result

Encode a token

Jwto.encode
3 | 	Jwt.HS256
4 | 	"secret"
5 | 	payload
val decode : string -> (t, string) Stdlib.result

Decode a token, this doesn't verify. Use decode_and_verify to do both

Jwto.decode "eyJhbGciOiJIUzI1NiJ9...."
val get_header : t -> header
val get_payload : t -> payload
val get_signature : t -> string
val decode_and_verify : string -> string -> (t, string) Stdlib.result

Decode and verify

Jwto.decode_and_verify
6 |     "secret" "eyJhbGciOiJIUzI1NiJ9...."
val is_valid : string -> t -> bool

Check if a token is valid. The first argument is the secret.

Jwto.is_valid "secret" "eyJhbGciOiJIUzI1NiJ9...."
val make_header : algorithm -> header

Make a header given the algorithm

val make_unsigned_token : header -> payload -> unsigned_token

Make an unsigned token

val make_signed_token : string -> unsigned_token -> (t, string) Stdlib.result

Make a signed token. This can fail if Base64 fails to encode the header or payload.

unsigned_token
7 | |> Jwto.make_signed_token "secret"
val pp : Stdlib.Format.formatter -> t -> unit

Pretty print a token

val eq : t -> t -> bool

Compare two tokens

val algorithm_to_string : algorithm -> string

Converts the algorithm type to a string

val show_algorithm : algorithm -> string
val header_to_string : header -> string

Encode the header as a JSON string

val show_header : header -> string
val payload_to_string : payload -> string

Encode the payload as a JSON string

val show_payload : payload -> string
val get_claim : string -> payload -> string option

Get a claim from the payload

payload
8 | |> Jwto.get_claim "email"
-------------------------------------------------------------------------------- /docs/jwto/index.html: -------------------------------------------------------------------------------- 1 | 2 | index (jwto.index)

jwto index

Library jwto

The entry point of this library is the module: Jwto.

-------------------------------------------------------------------------------- /docs/odoc.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | /* Copyright (c) 2016 The odoc contributors. All rights reserved. 3 | Distributed under the ISC license, see terms at the end of the file. 4 | odoc 1.5.3 */ 5 | 6 | /* Fonts */ 7 | @import url('https://fonts.googleapis.com/css?family=Fira+Mono:400,500'); 8 | @import url('https://fonts.googleapis.com/css?family=Noticia+Text:400,400i,700'); 9 | @import url('https://fonts.googleapis.com/css?family=Fira+Sans:400,400i,500,500i,600,600i,700,700i'); 10 | 11 | 12 | /* Reset a few things. */ 13 | 14 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { 15 | margin: 0; 16 | padding: 0; 17 | border: 0; 18 | font-size: inherit; 19 | font: inherit; 20 | line-height: inherit; 21 | vertical-align: baseline; 22 | text-align: inherit; 23 | color: inherit; 24 | background: transparent; 25 | } 26 | 27 | table { 28 | border-collapse: collapse; 29 | border-spacing: 0; 30 | } 31 | 32 | *, *:before, *:after { 33 | box-sizing: border-box; 34 | } 35 | 36 | html { 37 | font-size: 15px; 38 | } 39 | 40 | body { 41 | font-family: "Fira Sans", Helvetica, Arial, sans-serif; 42 | text-align: left; 43 | color: #333; 44 | background: #FFFFFF; 45 | } 46 | 47 | .content { 48 | max-width: 90ex; 49 | margin-left: calc(10vw + 20ex); 50 | margin-right: 4ex; 51 | margin-top: 20px; 52 | margin-bottom: 50px; 53 | font-family: "Noticia Text", Georgia, serif; 54 | line-height: 1.5; 55 | } 56 | 57 | .content>header { 58 | margin-bottom: 30px; 59 | } 60 | 61 | .content>header nav { 62 | font-family: "Fira Sans", Helvetica, Arial, sans-serif; 63 | } 64 | 65 | /* Basic markup elements */ 66 | 67 | b, strong { 68 | font-weight: 500; 69 | } 70 | 71 | i, em { 72 | font-style: italic; 73 | } 74 | 75 | sup { 76 | vertical-align: super; 77 | } 78 | 79 | sub { 80 | vertical-align: sub; 81 | } 82 | 83 | sup, sub { 84 | font-size: 12px; 85 | line-height: 0; 86 | margin-left: 0.2ex; 87 | } 88 | 89 | pre { 90 | margin-top: 0.8em; 91 | margin-bottom: 1.2em; 92 | } 93 | 94 | p, ul, ol { 95 | margin-top: 0.5em; 96 | margin-bottom: 1em; 97 | } 98 | ul, ol { 99 | list-style-position: outside 100 | } 101 | 102 | ul>li { 103 | margin-left: 22px; 104 | } 105 | 106 | ol>li { 107 | margin-left: 27.2px; 108 | } 109 | 110 | li>*:first-child { 111 | margin-top: 0 112 | } 113 | 114 | /* Text alignements, this should be forbidden. */ 115 | 116 | .left { 117 | text-align: left; 118 | } 119 | 120 | .right { 121 | text-align: right; 122 | } 123 | 124 | .center { 125 | text-align: center; 126 | } 127 | 128 | /* Links and anchors */ 129 | 130 | a { 131 | text-decoration: none; 132 | color: #2C5CBD; 133 | } 134 | 135 | a:hover { 136 | box-shadow: 0 1px 0 0 #2C5CBD; 137 | } 138 | 139 | /* Linked highlight */ 140 | *:target { 141 | background-color: rgba(187,239,253,0.3) !important; 142 | box-shadow: 0 0px 0 1px rgba(187,239,253,0.8) !important; 143 | border-radius: 1px; 144 | } 145 | 146 | *:hover>a.anchor { 147 | visibility: visible; 148 | } 149 | 150 | a.anchor:before { 151 | content: "#" 152 | } 153 | 154 | a.anchor:hover { 155 | box-shadow: none; 156 | text-decoration: none; 157 | color: #555; 158 | } 159 | 160 | a.anchor { 161 | visibility: hidden; 162 | position: absolute; 163 | /* top: 0px; */ 164 | /* margin-left: -3ex; */ 165 | margin-left: -1.3em; 166 | font-weight: normal; 167 | font-style: normal; 168 | padding-right: 0.4em; 169 | padding-left: 0.4em; 170 | /* To remain selectable */ 171 | color: #d5d5d5; 172 | } 173 | 174 | .spec > a.anchor { 175 | margin-left: -2.3em; 176 | padding-right: 0.9em; 177 | } 178 | 179 | .xref-unresolved { 180 | color: #2C5CBD; 181 | } 182 | .xref-unresolved:hover { 183 | box-shadow: 0 1px 0 0 #CC6666; 184 | } 185 | 186 | /* Section and document divisions. 187 | Until at least 4.03 many of the modules of the stdlib start at .h7, 188 | we restart the sequence there like h2 */ 189 | 190 | h1, h2, h3, h4, h5, h6, .h7, .h8, .h9, .h10 { 191 | font-family: "Fira Sans", Helvetica, Arial, sans-serif; 192 | font-weight: 400; 193 | margin: 0.5em 0 0.5em 0; 194 | padding-top: 0.1em; 195 | line-height: 1.2; 196 | overflow-wrap: break-word; 197 | } 198 | 199 | h1 { 200 | font-weight: 500; 201 | font-size: 2.441em; 202 | margin-top: 1.214em; 203 | } 204 | 205 | h1 { 206 | font-weight: 500; 207 | font-size: 1.953em; 208 | box-shadow: 0 1px 0 0 #ddd; 209 | } 210 | 211 | h2 { 212 | font-size: 1.563em; 213 | } 214 | 215 | h3 { 216 | font-size: 1.25em; 217 | } 218 | 219 | small, .font_small { 220 | font-size: 0.8em; 221 | } 222 | 223 | h1 code, h1 tt { 224 | font-size: inherit; 225 | font-weight: inherit; 226 | } 227 | 228 | h2 code, h2 tt { 229 | font-size: inherit; 230 | font-weight: inherit; 231 | } 232 | 233 | h3 code, h3 tt { 234 | font-size: inherit; 235 | font-weight: inherit; 236 | } 237 | 238 | h3 code, h3 tt { 239 | font-size: inherit; 240 | font-weight: inherit; 241 | } 242 | 243 | h4 { 244 | font-size: 1.12em; 245 | } 246 | 247 | 248 | /* Preformatted and code */ 249 | 250 | tt, code, pre { 251 | font-family: "Fira Mono", courier; 252 | font-weight: 400; 253 | } 254 | 255 | pre { 256 | padding: 0.1em; 257 | border: 1px solid #eee; 258 | border-radius: 5px; 259 | overflow-x: auto; 260 | } 261 | 262 | p code, li code { 263 | background-color: #f6f8fa; 264 | color: #0d2b3e; 265 | border-radius: 3px; 266 | padding: 0 0.3ex; 267 | } 268 | 269 | p a > code { 270 | color: #2C5CBD; 271 | } 272 | 273 | /* Code blocks (e.g. Examples) */ 274 | 275 | pre code { 276 | font-size: 0.893rem; 277 | } 278 | 279 | /* Code lexemes */ 280 | 281 | .keyword { 282 | font-weight: 500; 283 | } 284 | 285 | /* Module member specification */ 286 | 287 | .spec:not(.include), .spec.include details summary { 288 | background-color: #f6f8fa; 289 | border-radius: 3px; 290 | border-left: 4px solid #5c9cf5; 291 | border-right: 5px solid transparent; 292 | padding: 0.35em 0.5em; 293 | } 294 | 295 | .spec.include details summary:hover { 296 | background-color: #ebeff2; 297 | } 298 | 299 | dl, div.spec, .doc, aside { 300 | margin-bottom: 20px; 301 | } 302 | 303 | dl > dd { 304 | padding: 0.5em; 305 | } 306 | 307 | dd> :first-child { 308 | margin-top: 0; 309 | } 310 | 311 | dl:last-child, dd> :last-child, aside:last-child, article:last-child { 312 | margin-bottom: 0; 313 | } 314 | 315 | dt+dt { 316 | margin-top: 15px; 317 | } 318 | 319 | section+section, section > header + dl { 320 | margin-top: 25px; 321 | } 322 | 323 | .spec.type .variant { 324 | margin-left: 2ch; 325 | } 326 | .spec.type .variant p { 327 | margin: 0; 328 | font-style: italic; 329 | } 330 | .spec.type .record { 331 | margin-left: 2ch; 332 | } 333 | .spec.type .record p { 334 | margin: 0; 335 | font-style: italic; 336 | } 337 | 338 | div.def { 339 | margin-top: 0; 340 | text-indent: -2ex; 341 | padding-left: 2ex; 342 | } 343 | 344 | div.def+div.doc { 345 | margin-left: 1ex; 346 | margin-top: 2.5px 347 | } 348 | 349 | div.doc>*:first-child { 350 | margin-top: 0; 351 | } 352 | 353 | /* The elements other than heading should be wrapped in