diff --git a/404.html b/404.html index 4c36cb7..3babe82 100644 --- a/404.html +++ b/404.html @@ -11,7 +11,7 @@ - + @@ -19,7 +19,7 @@ - + @@ -372,7 +372,7 @@ - + diff --git a/assets/javascripts/bundle.19047be9.min.js b/assets/javascripts/bundle.407015b8.min.js similarity index 73% rename from assets/javascripts/bundle.19047be9.min.js rename to assets/javascripts/bundle.407015b8.min.js index 0e09ba9..4361bb7 100644 --- a/assets/javascripts/bundle.19047be9.min.js +++ b/assets/javascripts/bundle.407015b8.min.js @@ -24,6 +24,6 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */var wr=function(e,t){return wr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(r[o]=n[o])},wr(e,t)};function ie(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");wr(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}function un(e,t,r,n){function o(i){return i instanceof r?i:new r(function(s){s(i)})}return new(r||(r=Promise))(function(i,s){function a(u){try{f(n.next(u))}catch(p){s(p)}}function c(u){try{f(n.throw(u))}catch(p){s(p)}}function f(u){u.done?i(u.value):o(u.value).then(a,c)}f((n=n.apply(e,t||[])).next())})}function $t(e,t){var r={label:0,sent:function(){if(i[0]&1)throw i[1];return i[1]},trys:[],ops:[]},n,o,i,s;return s={next:a(0),throw:a(1),return:a(2)},typeof Symbol=="function"&&(s[Symbol.iterator]=function(){return this}),s;function a(f){return function(u){return c([f,u])}}function c(f){if(n)throw new TypeError("Generator is already executing.");for(;r;)try{if(n=1,o&&(i=f[0]&2?o.return:f[0]?o.throw||((i=o.return)&&i.call(o),0):o.next)&&!(i=i.call(o,f[1])).done)return i;switch(o=0,i&&(f=[f[0]&2,i.value]),f[0]){case 0:case 1:i=f;break;case 4:return r.label++,{value:f[1],done:!1};case 5:r.label++,o=f[1],f=[0];continue;case 7:f=r.ops.pop(),r.trys.pop();continue;default:if(i=r.trys,!(i=i.length>0&&i[i.length-1])&&(f[0]===6||f[0]===2)){r=0;continue}if(f[0]===3&&(!i||f[1]>i[0]&&f[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||a(m,d)})})}function a(m,d){try{c(n[m](d))}catch(h){p(i[0][3],h)}}function c(m){m.value instanceof et?Promise.resolve(m.value.v).then(f,u):p(i[0][2],m)}function f(m){a("next",m)}function u(m){a("throw",m)}function p(m,d){m(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function ln(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),o(a,c,s.done,s.value)})}}function o(i,s,a,c){Promise.resolve(c).then(function(f){i({value:f,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: `+r.map(function(n,o){return o+1+") "+n.toString()}).join(` - `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),c=a.next();!c.done;c=a.next()){var f=c.value;f.remove(this)}}catch(v){t={error:v}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var u=this.initialTeardown;if(C(u))try{u()}catch(v){i=v instanceof It?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=Ee(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{mn(h)}catch(v){i=i!=null?i:[],v instanceof It?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)mn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Sr=Ie.EMPTY;function jt(e){return e instanceof Ie||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function mn(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,s=o.isStopped,a=o.observers;return i||s?Sr:(this.currentObservers=null,a.push(r),new Ie(function(){n.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,s=n.isStopped;o?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,n){return new En(r,n)},t}(F);var En=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Sr},t}(x);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,s=n._infiniteTimeWindow,a=n._timestampProvider,c=n._windowTime;o||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,s=o._buffer,a=s.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var s=r.actions;n!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Wt);var Tn=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Dt);var Te=new Tn(Sn);var _=new F(function(e){return e.complete()});function Vt(e){return e&&C(e.schedule)}function Cr(e){return e[e.length-1]}function Ye(e){return C(Cr(e))?e.pop():void 0}function Oe(e){return Vt(Cr(e))?e.pop():void 0}function zt(e,t){return typeof Cr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Nt(e){return C(e==null?void 0:e.then)}function qt(e){return C(e[ft])}function Kt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ni(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Yt=Ni();function Gt(e){return C(e==null?void 0:e[Yt])}function Bt(e){return pn(this,arguments,function(){var r,n,o,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,et(r.read())];case 3:return n=s.sent(),o=n.value,i=n.done,i?[4,et(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,et(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Jt(e){return C(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(qt(e))return qi(e);if(pt(e))return Ki(e);if(Nt(e))return Qi(e);if(Kt(e))return On(e);if(Gt(e))return Yi(e);if(Jt(e))return Gi(e)}throw Qt(e)}function qi(e){return new F(function(t){var r=e[ft]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Ki(e){return new F(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?A(function(o,i){return e(o,i,n)}):de,ge(1),r?He(t):Vn(function(){return new Zt}))}}function zn(){for(var e=[],t=0;t=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(f){var u,p,m,d=0,h=!1,v=!1,G=function(){p==null||p.unsubscribe(),p=void 0},oe=function(){G(),u=m=void 0,h=v=!1},N=function(){var T=u;oe(),T==null||T.unsubscribe()};return y(function(T,Qe){d++,!v&&!h&&G();var De=m=m!=null?m:r();Qe.add(function(){d--,d===0&&!v&&!h&&(p=$r(N,c))}),De.subscribe(Qe),!u&&d>0&&(u=new rt({next:function($e){return De.next($e)},error:function($e){v=!0,G(),p=$r(oe,o,$e),De.error($e)},complete:function(){h=!0,G(),p=$r(oe,s),De.complete()}}),U(T).subscribe(u))})(f)}}function $r(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function z(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),V(e===_e()),B())}function Xe(e){return{x:e.offsetLeft,y:e.offsetTop}}function Qn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,Te),l(()=>Xe(e)),V(Xe(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,Te),l(()=>rr(e)),V(rr(e)))}var Gn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!Dr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),ga?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!Dr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=va.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Bn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Xn=typeof WeakMap!="undefined"?new WeakMap:new Gn,Zn=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=ya.getInstance(),n=new Aa(t,r,this);Xn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){Zn.prototype[e]=function(){var t;return(t=Xn.get(this))[e].apply(t,arguments)}});var Ca=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:Zn}(),eo=Ca;var to=new x,Ra=$(()=>k(new eo(e=>{for(let t of e)to.next(t)}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),J(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return Ra.pipe(S(t=>t.observe(e)),g(t=>to.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var ro=new x,ka=$(()=>k(new IntersectionObserver(e=>{for(let t of e)ro.next(t)},{threshold:0}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),J(1));function sr(e){return ka.pipe(S(t=>t.observe(e)),g(t=>ro.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function no(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),B())}var cr={drawer:z("[data-md-toggle=drawer]"),search:z("[data-md-toggle=search]")};function oo(e){return cr[e].checked}function Ke(e,t){cr[e].checked!==t&&cr[e].click()}function Ue(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),V(t.checked))}function Ha(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Pa(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function io(){let e=b(window,"keydown").pipe(A(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:oo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),A(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!Ha(n,r)}return!0}),pe());return Pa().pipe(g(t=>t?_:e))}function le(){return new URL(location.href)}function ot(e){location.href=e.href}function ao(){return new x}function so(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)so(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)so(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function co(){return location.hash.substring(1)}function Vr(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function $a(e){return L(b(window,"hashchange"),e).pipe(l(co),V(co()),A(t=>t.length>0),J(1))}function fo(e){return $a(e).pipe(l(t=>ce(`[id="${t}"]`)),A(t=>typeof t!="undefined"))}function zr(e){let t=matchMedia(e);return er(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function uo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function Nr(e,t){return e.pipe(g(r=>r?t():_))}function ur(e,t={credentials:"same-origin"}){return ue(fetch(`${e}`,t)).pipe(fe(()=>_),g(r=>r.status!==200?Tt(()=>new Error(r.statusText)):k(r)))}function We(e,t){return ur(e,t).pipe(g(r=>r.json()),J(1))}function po(e,t){let r=new DOMParser;return ur(e,t).pipe(g(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),J(1))}function pr(e){let t=M("script",{src:e});return $(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(g(()=>Tt(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),R(()=>document.head.removeChild(t)),ge(1))))}function lo(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function mo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(lo),V(lo()))}function ho(){return{width:innerWidth,height:innerHeight}}function bo(){return b(window,"resize",{passive:!0}).pipe(l(ho),V(ho()))}function vo(){return Q([mo(),bo()]).pipe(l(([e,t])=>({offset:e,size:t})),J(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(Z("size")),o=Q([n,r]).pipe(l(()=>Xe(e)));return Q([r,t,o]).pipe(l(([{height:i},{offset:s,size:a},{x:c,y:f}])=>({offset:{x:s.x-c,y:s.y-f+i},size:a})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(s=>{let a=document.createElement("script");a.src=i,a.onload=s,document.body.appendChild(a)})),Promise.resolve())}var r=class extends EventTarget{constructor(n){super(),this.url=n,this.m=i=>{i.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:i.data})),this.onmessage&&this.onmessage(i))},this.e=(i,s,a,c,f)=>{if(s===`${this.url}`){let u=new ErrorEvent("error",{message:i,filename:s,lineno:a,colno:c,error:f});this.dispatchEvent(u),this.onerror&&this.onerror(u)}};let o=document.createElement("iframe");o.hidden=!0,document.body.appendChild(this.iframe=o),this.w.document.open(),this.w.document.write(` - + diff --git a/search/search_index.json b/search/search_index.json index 197ff00..838766e 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Getting Started","text":"

Welcome to jwt-go, a go (or 'golang' for search engine friendliness) implementation of JSON Web Tokens.

"},{"location":"#supported-go-versions","title":"Supported Go versions","text":"

Our support of Go versions is aligned with Go's version release policy. So we will support a major version of Go until there are two newer major releases. We no longer support building jwt-go with unsupported Go versions, as these contain security vulnerabilities which will not be fixed.

"},{"location":"#what-the-heck-is-a-jwt","title":"What the heck is a JWT?","text":"

JWT.io has a great introduction to JSON Web Tokens.

In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for Bearer tokens in OAuth 2.0 A token is made of three parts, separated by .'s. The first two parts are JSON objects, that have been base64url encoded. The last part is the signature, encoded the same way.

The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used.

The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to RFC 7519 for information about reserved keys and the proper way to add your own.

"},{"location":"#whats-in-the-box","title":"What's in the box?","text":"

This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, ECDSA and EdDSA, though hooks are present for adding your own.

"},{"location":"#installation-guidelines","title":"Installation Guidelines","text":"

To install the jwt package, you first need to have Go installed, then you can use the command below to add jwt-go as a dependency in your Go program.

go get -u github.com/golang-jwt/jwt/v5\n

Then import it in your code:

import \"github.com/golang-jwt/jwt/v5\"\n
"},{"location":"#jwt-and-oauth-20","title":"JWT and OAuth 2.0","text":"

It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication.

Without going too far down the rabbit hole, here's a description of the interaction of these technologies:

  • OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth.
  • OAuth defines several options for passing around authentication data. One popular method is called a \"bearer token\". A bearer token is simply a string that should only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token. This is also specified in the JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens, detailed in RFC 9068.
  • Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over TLS.
"},{"location":"usage/create/","title":"Creating a New JWT","text":"

One of the primary goals of this library is to create a new JWT (or in short token).

"},{"location":"usage/create/#with-default-options","title":"With Default Options","text":"

The easiest way to create a token is to use the jwt.New function. It then needs one of the available signing methods, to finally sign and convert the token into a string format (using the SignedString method). In the first example, we are using a symmetric signing method, i.e., HS256. For a symmetric method, both the signing and the verifying key are the same and thus, both must be equally protected (and should definitely NOT be stored in your code).

var (\nkey []byte\nt   *jwt.Token\ns   string\n)\nkey = /* Load key from somewhere, for example an environment variable */\nt = jwt.New(jwt.SigningMethodHS256) // (1)!\ns = t.SignedString(key) // (2)!\n
  1. This initializes a new jwt.Token struct based on the supplied signing method. In this case a symmetric method is chosen.
  2. This step computes a cryptographic signature based on the supplied key.

Signing using an asymmetric signing method (for example ECDSA) works quite similar. For an asymmetric method, the private key (which must be kept secret) is used to sign and the corresponding public key (which can be freely transmitted) is used to verify the token.

var (\nkey *ecdsa.PrivateKey\nt   *jwt.Token\ns   string\n)\nkey = /* Load key from somewhere, for example a file */\nt = jwt.New(jwt.SigningMethodES256) // (1)!\ns = t.SignedString(key) // (2)!\n
  1. This initializes a new jwt.Token struct based on the supplied signing method. In this case a asymmetric method is chosen.
  2. This step computes a cryptographic signature based on the supplied private key.

Note, that the chosen signing method and the type of key must match. Please refer to Signing Methods for a complete overview.

"},{"location":"usage/create/#with-additional-claims","title":"With Additional Claims","text":"

While the step above using jwt.New creates a valid token, it contains an empty set of claims. Claims are certain pieces of information that are encoded into the token. Since they are encoded and signed by the issuer of the token, one can assume that this information is valid (in the scope of the issuer). Claims can be used to provide the basis for user authentication or authorization, e.g., by encoding a user name or ID or roles into the token. This is also commonly in combination with OAuth 2.0. Furthermore, claims can also contain certain metadata about the token itself, e.g., the time until which the token is regarded as valid and not expired.

RFC 7519 provides a list of so called registered claim names 1, which each JWT parser needs to understand. Using the jwt.NewWithClaims, a token with different claims can be created.

var (\nkey *ecdsa.PrivateKey\nt   *jwt.Token\ns   string\n)\nkey = /* Load key from somewhere, for example a file */\nt = jwt.NewWithClaims(jwt.SigningMethodES256, // (1)!\njwt.MapClaims{ // (2)!\n\"iss\": \"my-auth-server\", // (3)!\n\"sub\": \"john\", // (4)!\n\"foo\": 2, // (5)!\n})\ns = t.SignedString(key) // (6)!\n
  1. This initializes a new jwt.Token struct based on the supplied signing method. In this case a asymmetric method is chosen, which is the first parameter.
  2. The second parameter contains the desired claims in form of the jwt.Claims interface. In this case jwt.MapClaims are used, which is a wrapper type around a Go map containing string keys.
  3. The \"sub\"3 claim is a registered claim name that contains the subject this token identifies, e.g. a user name. More technical, this claim identifies the principal that is the subject of the token.
  4. The \"iss\"2 claim is a registered claim name that contains the issuer of the token. More technical, this claim identifies the principal that issued the token.
  5. The \"foo\" claim is a custom claim containing a numeric value. Any string value can be chosen as a claim name, as long as it does not interfere with a registered claim name.
  6. This step computes a cryptographic signature based on the supplied private key.
"},{"location":"usage/create/#with-options","title":"With Options","text":"

While we already prepared a jwt.TokenOption type, which can be supplied as a varargs to jwt.New and jwt.NewWithClaims, these are strictly for future compatibility and are currently not used.

  1. Section 4.1 of RFC 7519 \u21a9

  2. Section 4.1.1 of RFC 7519 \u21a9

  3. Section 4.1.2 of RFC 7519 \u21a9

"},{"location":"usage/parse/","title":"Parsing and Validating a JWT","text":""},{"location":"usage/parse/#keyfunc","title":"Keyfunc","text":""},{"location":"usage/parse/#with-options","title":"With Options","text":"Option Name Arguments Description WithValidMethods methods as []string Supplies a list of signing methods that the parser will check against the algorithm on the token. Only the supplied methods will be considered valid. It is heavily encouraged to use this option in order to prevent \"none\" algorithm attacks.1 WithJSONNumber - Configures the underlying JSON parser to use the UseNumber function, which decodes numeric JSON values into the json.Number type instead of float64. This type can then be used to convert the value into either a floating type or integer type. WithIssuer issuer as string Configures the validator to require the specified issuer in the \"iss\"2 claim. Validation will fail if a different issuer is specified in the token or the \"iss\" claim is missing. WithSubject subject as string Configures the validator to require the specified subject in the \"sub\"3 claim. Validation will fail if a different subject is specified in the token or the \"sub\" claim is missing. WithAudience audience as string Configures the validator to require the specified audience in the \"aud\"4 claim. Validation will fail if the audience is not listed in the token or the \"aud\" claim is missing. The contents of the audience string is application specific, but often contains the URI of the service that consumes the token. WithLeeway leeway as time.Duration According to the RFC, a certain time window (leeway) is allowed when verifying time based claims, such as expiration time. This is due to the fact that a there is not perfect clock synchronization on the a distributed system such as the internet. While we do not enforce any restriction on the amount of leeway, it should generally not exceed more than a few minutes.5 WithIssuedAt - Enables a sanity check of the \"iat\"6 claim. More specifically, when turning this option on, the validator will check if the issued-at time is not in the future. Danger Zone
  1. https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries \u21a9

  2. Section 4.1.1 of RFC 7519 \u21a9

  3. Section 4.1.2 of RFC 7519 \u21a9

  4. Section 4.1.3 of RFC 7519 \u21a9

  5. Section 4.1.4 of RFC 7519 \u21a9

  6. Section 4.1.6 of RFC 7519 \u21a9

"},{"location":"usage/signing_methods/","title":"Signing Methods","text":""},{"location":"usage/signing_methods/#signing-vs-encryption","title":"Signing vs Encryption","text":"

A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data:

  • The author of the token was in the possession of the signing secret
  • The data has not been modified since it was signed

It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, JSON Web Encryption (JWT)1, that provides this functionality. The companion project https://github.com/golang-jwt/jwe aims at a (very) experimental implementation of the JWE standard.

"},{"location":"usage/signing_methods/#choosing-a-signing-method","title":"Choosing a Signing Method","text":"

There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric.

Symmetric signing methods, such as HMAC, use only a single secret. This is probably the simplest signing method to use since any []byte can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation.

Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification.

"},{"location":"usage/signing_methods/#signing-methods-and-key-types","title":"Signing Methods and Key Types","text":"

Each signing method expects a different object type for its signing keys. The following table lists all supported signing methods in the core library.

Name \"alg\" Parameter Values Signing Key Type Verification Key Type HMAC signing method2 HS256,HS384,HS512 []byte []byte RSA signing method3 RS256,RS384,RS512 *rsa.PrivateKey *rsa.PublicKey ECDSA signing method4 ES256,ES384,ES512 *ecdsa.PrivateKey *ecdsa.PublicKey RSA-PSS signing method5 PS256,PS384,PS512 *rsa.PrivateKey *rsa.PublicKey EdDSA signing method6 Ed25519 ed25519.PrivateKey ed25519.PublicKey
  1. RFC 7516 \u21a9

  2. Section 3.2 of RFC 7518 \u21a9

  3. Section 3.2 of RFC 7518 \u21a9

  4. Section 3.2 of RFC 7518 \u21a9

  5. Section 3.2 of RFC 7518 \u21a9

  6. Section 3.1 of RFC 8037 \u21a9

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Getting Started","text":"

Welcome to jwt-go, a go (or 'golang' for search engine friendliness) implementation of JSON Web Tokens.

"},{"location":"#supported-go-versions","title":"Supported Go versions","text":"

Our support of Go versions is aligned with Go's version release policy. So we will support a major version of Go until there are two newer major releases. We no longer support building jwt-go with unsupported Go versions, as these contain security vulnerabilities which will not be fixed.

"},{"location":"#what-the-heck-is-a-jwt","title":"What the heck is a JWT?","text":"

JWT.io has a great introduction to JSON Web Tokens.

In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for Bearer tokens in OAuth 2.0 A token is made of three parts, separated by .'s. The first two parts are JSON objects, that have been base64url encoded. The last part is the signature, encoded the same way.

The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used.

The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to RFC 7519 for information about reserved keys and the proper way to add your own.

"},{"location":"#whats-in-the-box","title":"What's in the box?","text":"

This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, ECDSA and EdDSA, though hooks are present for adding your own.

"},{"location":"#installation-guidelines","title":"Installation Guidelines","text":"

To install the jwt package, you first need to have Go installed, then you can use the command below to add jwt-go as a dependency in your Go program.

go get -u github.com/golang-jwt/jwt/v5\n

Then import it in your code:

import \"github.com/golang-jwt/jwt/v5\"\n
"},{"location":"#jwt-and-oauth-20","title":"JWT and OAuth 2.0","text":"

It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication.

Without going too far down the rabbit hole, here's a description of the interaction of these technologies:

  • OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth.
  • OAuth defines several options for passing around authentication data. One popular method is called a \"bearer token\". A bearer token is simply a string that should only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token. This is also specified in the JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens, detailed in RFC 9068.
  • Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over TLS.
"},{"location":"usage/create/","title":"Creating a New JWT","text":"

One of the primary goals of this library is to create a new JWT (or in short token).

"},{"location":"usage/create/#with-default-options","title":"With Default Options","text":"

The easiest way to create a token is to use the jwt.New function. It then needs one of the available signing methods, to finally sign and convert the token into a string format (using the SignedString method). In the first example, we are using a symmetric signing method, i.e., HS256. For a symmetric method, both the signing and the verifying key are the same and thus, both must be equally protected (and should definitely NOT be stored in your code).

var (\nkey []byte\nt   *jwt.Token\ns   string\n)\nkey = /* Load key from somewhere, for example an environment variable */\nt = jwt.New(jwt.SigningMethodHS256) // (1)!\ns = t.SignedString(key) // (2)!\n
  1. This initializes a new jwt.Token struct based on the supplied signing method. In this case a symmetric method is chosen.
  2. This step computes a cryptographic signature based on the supplied key.

Signing using an asymmetric signing method (for example ECDSA) works quite similar. For an asymmetric method, the private key (which must be kept secret) is used to sign and the corresponding public key (which can be freely transmitted) is used to verify the token.

var (\nkey *ecdsa.PrivateKey\nt   *jwt.Token\ns   string\n)\nkey = /* Load key from somewhere, for example a file */\nt = jwt.New(jwt.SigningMethodES256) // (1)!\ns = t.SignedString(key) // (2)!\n
  1. This initializes a new jwt.Token struct based on the supplied signing method. In this case a asymmetric method is chosen.
  2. This step computes a cryptographic signature based on the supplied private key.

Note, that the chosen signing method and the type of key must match. Please refer to Signing Methods for a complete overview.

"},{"location":"usage/create/#with-additional-claims","title":"With Additional Claims","text":"

While the step above using jwt.New creates a valid token, it contains an empty set of claims. Claims are certain pieces of information that are encoded into the token. Since they are encoded and signed by the issuer of the token, one can assume that this information is valid (in the scope of the issuer). Claims can be used to provide the basis for user authentication or authorization, e.g., by encoding a user name or ID or roles into the token. This is also commonly in combination with OAuth 2.0. Furthermore, claims can also contain certain metadata about the token itself, e.g., the time until which the token is regarded as valid and not expired.

RFC 7519 provides a list of so called registered claim names 1, which each JWT parser needs to understand. Using the jwt.NewWithClaims, a token with different claims can be created.

var (\nkey *ecdsa.PrivateKey\nt   *jwt.Token\ns   string\n)\nkey = /* Load key from somewhere, for example a file */\nt = jwt.NewWithClaims(jwt.SigningMethodES256, // (1)!\njwt.MapClaims{ // (2)!\n\"iss\": \"my-auth-server\", // (3)!\n\"sub\": \"john\", // (4)!\n\"foo\": 2, // (5)!\n})\ns = t.SignedString(key) // (6)!\n
  1. This initializes a new jwt.Token struct based on the supplied signing method. In this case a asymmetric method is chosen, which is the first parameter.
  2. The second parameter contains the desired claims in form of the jwt.Claims interface. In this case jwt.MapClaims are used, which is a wrapper type around a Go map containing string keys.
  3. The \"sub\"3 claim is a registered claim name that contains the subject this token identifies, e.g. a user name. More technical, this claim identifies the principal that is the subject of the token.
  4. The \"iss\"2 claim is a registered claim name that contains the issuer of the token. More technical, this claim identifies the principal that issued the token.
  5. The \"foo\" claim is a custom claim containing a numeric value. Any string value can be chosen as a claim name, as long as it does not interfere with a registered claim name.
  6. This step computes a cryptographic signature based on the supplied private key.
"},{"location":"usage/create/#with-options","title":"With Options","text":"

While we already prepared a jwt.TokenOption type, which can be supplied as a varargs to jwt.New and jwt.NewWithClaims, these are strictly for future compatibility and are currently not used.

  1. Section 4.1 of RFC 7519 \u21a9

  2. Section 4.1.1 of RFC 7519 \u21a9

  3. Section 4.1.2 of RFC 7519 \u21a9

"},{"location":"usage/parse/","title":"Parsing and Validating a JWT","text":""},{"location":"usage/parse/#keyfunc","title":"Keyfunc","text":""},{"location":"usage/parse/#with-options","title":"With Options","text":"Option Name Arguments Description WithValidMethods methods as []string Supplies a list of signing methods that the parser will check against the algorithm on the token. Only the supplied methods will be considered valid. It is heavily encouraged to use this option in order to prevent \"none\" algorithm attacks.1 WithJSONNumber - Configures the underlying JSON parser to use the UseNumber function, which decodes numeric JSON values into the json.Number type instead of float64. This type can then be used to convert the value into either a floating type or integer type. WithIssuer issuer as string Configures the validator to require the specified issuer in the \"iss\"2 claim. Validation will fail if a different issuer is specified in the token or the \"iss\" claim is missing. WithSubject subject as string Configures the validator to require the specified subject in the \"sub\"3 claim. Validation will fail if a different subject is specified in the token or the \"sub\" claim is missing. WithAudience audience as string Configures the validator to require the specified audience in the \"aud\"4 claim. Validation will fail if the audience is not listed in the token or the \"aud\" claim is missing. The contents of the audience string is application specific, but often contains the URI of the service that consumes the token. WithLeeway leeway as time.Duration According to the RFC, a certain time window (leeway) is allowed when verifying time based claims, such as expiration time. This is due to the fact that a there is not perfect clock synchronization on the a distributed system such as the internet. While we do not enforce any restriction on the amount of leeway, it should generally not exceed more than a few minutes.5 WithIssuedAt - Enables a sanity check of the \"iat\"6 claim. More specifically, when turning this option on, the validator will check if the issued-at time is not in the future. Danger Zone
  1. https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries \u21a9

  2. Section 4.1.1 of RFC 7519 \u21a9

  3. Section 4.1.2 of RFC 7519 \u21a9

  4. Section 4.1.3 of RFC 7519 \u21a9

  5. Section 4.1.4 of RFC 7519 \u21a9

  6. Section 4.1.6 of RFC 7519 \u21a9

"},{"location":"usage/signing_methods/","title":"Signing Methods","text":""},{"location":"usage/signing_methods/#signing-vs-encryption","title":"Signing vs Encryption","text":"

A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data:

  • The author of the token was in the possession of the signing secret
  • The data has not been modified since it was signed

It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, JSON Web Encryption (JWT)1, that provides this functionality. The companion project https://github.com/golang-jwt/jwe aims at a (very) experimental implementation of the JWE standard.

"},{"location":"usage/signing_methods/#choosing-a-signing-method","title":"Choosing a Signing Method","text":"

There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric.

Symmetric signing methods, such as HMAC, use only a single secret. This is probably the simplest signing method to use since any []byte can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation.

Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification.

"},{"location":"usage/signing_methods/#signing-methods-and-key-types","title":"Signing Methods and Key Types","text":"

Each signing method expects a different object type for its signing keys. The following table lists all supported signing methods in the core library.

Name \"alg\" Parameter Values Signing Key Type Verification Key Type HMAC signing method2 HS256,HS384,HS512 []byte []byte RSA signing method3 RS256,RS384,RS512 *rsa.PrivateKey *rsa.PublicKey ECDSA signing method4 ES256,ES384,ES512 *ecdsa.PrivateKey *ecdsa.PublicKey RSA-PSS signing method5 PS256,PS384,PS512 *rsa.PrivateKey *rsa.PublicKey EdDSA signing method6 Ed25519 ed25519.PrivateKey ed25519.PublicKey

Why is the HMAC signing method not accepting string as a key type?

We often get asked why the HMAC signing method only supports []byte and not string. This is intentionally and there are different reasons for doing so. First, we aim to use the key type that the underlying cryptographic operation in the Go library uses (this also applies to the other signing methods). In case of HMAC, this is hmac.New and it uses []byte as the type to represent a key.

Second, using string as a key type to represent a symmetric key can lead to unwanted situations. It gives the impression that this is something 'human readable' (like a password), but it is not. A symmetric key should contain as much entropy as possible and therefore include characters from the whole character set (even 'unreadable' ones) and ideally be generated by a cryptographic random source, such as rand.Read. Signing tokens with a cryptographically weak key will compromise the security of the tokens and in effect everything that depends on it, e.g., user authentication.

If you have trouble handling a []byte key in our setup, e.g., because you are reading it from your environment variables on your cluster or similar, you can always use base64 encoding to have the key as a \"string\" type outside of your program and then use base64.Encoding.DecodeString to decode the base64 string into the []byte slice that the signing method needs.

  1. RFC 7516 \u21a9

  2. Section 3.2 of RFC 7518 \u21a9

  3. Section 3.2 of RFC 7518 \u21a9

  4. Section 3.2 of RFC 7518 \u21a9

  5. Section 3.2 of RFC 7518 \u21a9

  6. Section 3.1 of RFC 8037 \u21a9

"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 6370336..6ac87ce 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,22 +2,22 @@ https://golang-jwt.github.io/jwt/ - 2023-03-30 + 2023-03-31 daily https://golang-jwt.github.io/jwt/usage/create/ - 2023-03-30 + 2023-03-31 daily https://golang-jwt.github.io/jwt/usage/parse/ - 2023-03-30 + 2023-03-31 daily https://golang-jwt.github.io/jwt/usage/signing_methods/ - 2023-03-30 + 2023-03-31 daily \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 936672b..32762ab 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ diff --git a/usage/create/index.html b/usage/create/index.html index fe47600..d1c3e68 100644 --- a/usage/create/index.html +++ b/usage/create/index.html @@ -17,7 +17,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -573,7 +573,7 @@ these are strictly for future compatibility and are currently not used.

- + diff --git a/usage/parse/index.html b/usage/parse/index.html index 31a9e38..f095c96 100644 --- a/usage/parse/index.html +++ b/usage/parse/index.html @@ -17,7 +17,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -545,7 +545,7 @@ - + diff --git a/usage/signing_methods/index.html b/usage/signing_methods/index.html index f904c29..9c5112f 100644 --- a/usage/signing_methods/index.html +++ b/usage/signing_methods/index.html @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + @@ -489,6 +489,10 @@ +

Why is the HMAC signing method not accepting string as a key type?

+

We often get asked why the HMAC signing method only supports []byte and not string. This is intentionally and there are different reasons for doing so. First, we aim to use the key type that the underlying cryptographic operation in the Go library uses (this also applies to the other signing methods). In case of HMAC, this is hmac.New and it uses []byte as the type to represent a key.

+

Second, using string as a key type to represent a symmetric key can lead to unwanted situations. It gives the impression that this is something 'human readable' (like a password), but it is not. A symmetric key should contain as much entropy as possible and therefore include characters from the whole character set (even 'unreadable' ones) and ideally be generated by a cryptographic random source, such as rand.Read. Signing tokens with a cryptographically weak key will compromise the security of the tokens and in effect everything that depends on it, e.g., user authentication.

+

If you have trouble handling a []byte key in our setup, e.g., because you are reading it from your environment variables on your cluster or similar, you can always use base64 encoding to have the key as a "string" type outside of your program and then use base64.Encoding.DecodeString to decode the base64 string into the []byte slice that the signing method needs.


    @@ -552,7 +556,7 @@ - +