You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
20 KiB
20 KiB
<!DOCTYPE html><html><head>
<title>readme</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="file:///c:\Users\Bas\.vscode\extensions\shd101wyy.markdown-preview-enhanced-0.6.0\node_modules\@shd101wyy\mume\dependencies\katex\katex.min.css">
<style>
/**
* prism.js Github theme based on GitHub's theme.
* @author Sam Clarke
*/
code[class*="language-"],
pre[class*="language-"] {
color: #333;
background: none;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.4;
-moz-tab-size: 8;
-o-tab-size: 8;
tab-size: 8;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: .8em;
overflow: auto;
/* border: 1px solid #ddd; */
border-radius: 3px;
/* background: #fff; */
background: #f5f5f5;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
background: #f5f5f5;
}
.token.comment,
.token.blockquote {
color: #969896;
}
.token.cdata {
color: #183691;
}
.token.doctype,
.token.punctuation,
.token.variable,
.token.macro.property {
color: #333;
}
.token.operator,
.token.important,
.token.keyword,
.token.rule,
.token.builtin {
color: #a71d5d;
}
.token.string,
.token.url,
.token.regex,
.token.attr-value {
color: #183691;
}
.token.property,
.token.number,
.token.boolean,
.token.entity,
.token.atrule,
.token.constant,
.token.symbol,
.token.command,
.token.code {
color: #0086b3;
}
.token.tag,
.token.selector,
.token.prolog {
color: #63a35c;
}
.token.function,
.token.namespace,
.token.pseudo-element,
.token.class,
.token.class-name,
.token.pseudo-class,
.token.id,
.token.url-reference .token.variable,
.token.attr-name {
color: #795da3;
}
.token.entity {
cursor: help;
}
.token.title,
.token.title .token.punctuation {
font-weight: bold;
color: #1d3e81;
}
.token.list {
color: #ed6a43;
}
.token.inserted {
background-color: #eaffea;
color: #55a532;
}
.token.deleted {
background-color: #ffecec;
color: #bd2c00;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
/* JSON */
.language-json .token.property {
color: #183691;
}
.language-markup .token.tag .token.punctuation {
color: #333;
}
/* CSS */
code.language-css,
.language-css .token.function {
color: #0086b3;
}
/* YAML */
.language-yaml .token.atrule {
color: #63a35c;
}
code.language-yaml {
color: #183691;
}
/* Ruby */
.language-ruby .token.function {
color: #333;
}
/* Markdown */
.language-markdown .token.url {
color: #795da3;
}
/* Makefile */
.language-makefile .token.symbol {
color: #795da3;
}
.language-makefile .token.variable {
color: #183691;
}
.language-makefile .token.builtin {
color: #0086b3;
}
/* Bash */
.language-bash .token.keyword {
color: #0086b3;
}
/* highlight */
pre[data-line] {
position: relative;
padding: 1em 0 1em 3em;
}
pre[data-line] .line-highlight-wrapper {
position: absolute;
top: 0;
left: 0;
background-color: transparent;
display: block;
width: 100%;
}
pre[data-line] .line-highlight {
position: absolute;
left: 0;
right: 0;
padding: inherit 0;
margin-top: 1em;
background: hsla(24, 20%, 50%,.08);
background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
pointer-events: none;
line-height: inherit;
white-space: pre;
}
pre[data-line] .line-highlight:before,
pre[data-line] .line-highlight[data-end]:after {
content: attr(data-start);
position: absolute;
top: .4em;
left: .6em;
min-width: 1em;
padding: 0 .5em;
background-color: hsla(24, 20%, 50%,.4);
color: hsl(24, 20%, 95%);
font: bold 65%/1.5 sans-serif;
text-align: center;
vertical-align: .3em;
border-radius: 999px;
text-shadow: none;
box-shadow: 0 1px white;
}
pre[data-line] .line-highlight[data-end]:after {
content: attr(data-end);
top: auto;
bottom: .4em;
}html body{font-family:"Helvetica Neue",Helvetica,"Segoe UI",Arial,freesans,sans-serif;font-size:16px;line-height:1.6;color:#333;background-color:#fff;overflow:initial;box-sizing:border-box;word-wrap:break-word}html body>:first-child{margin-top:0}html body h1,html body h2,html body h3,html body h4,html body h5,html body h6{line-height:1.2;margin-top:1em;margin-bottom:16px;color:#000}html body h1{font-size:2.25em;font-weight:300;padding-bottom:.3em}html body h2{font-size:1.75em;font-weight:400;padding-bottom:.3em}html body h3{font-size:1.5em;font-weight:500}html body h4{font-size:1.25em;font-weight:600}html body h5{font-size:1.1em;font-weight:600}html body h6{font-size:1em;font-weight:600}html body h1,html body h2,html body h3,html body h4,html body h5{font-weight:600}html body h5{font-size:1em}html body h6{color:#5c5c5c}html body strong{color:#000}html body del{color:#5c5c5c}html body a:not([href]){color:inherit;text-decoration:none}html body a{color:#08c;text-decoration:none}html body a:hover{color:#00a3f5;text-decoration:none}html body img{max-width:100%}html body>p{margin-top:0;margin-bottom:16px;word-wrap:break-word}html body>ul,html body>ol{margin-bottom:16px}html body ul,html body ol{padding-left:2em}html body ul.no-list,html body ol.no-list{padding:0;list-style-type:none}html body ul ul,html body ul ol,html body ol ol,html body ol ul{margin-top:0;margin-bottom:0}html body li{margin-bottom:0}html body li.task-list-item{list-style:none}html body li>p{margin-top:0;margin-bottom:0}html body .task-list-item-checkbox{margin:0 .2em .25em -1.8em;vertical-align:middle}html body .task-list-item-checkbox:hover{cursor:pointer}html body blockquote{margin:16px 0;font-size:inherit;padding:0 15px;color:#5c5c5c;background-color:#f0f0f0;border-left:4px solid #d6d6d6}html body blockquote>:first-child{margin-top:0}html body blockquote>:last-child{margin-bottom:0}html body hr{height:4px;margin:32px 0;background-color:#d6d6d6;border:0 none}html body table{margin:10px 0 15px 0;border-collapse:collapse;border-spacing:0;display:block;width:100%;overflow:auto;word-break:normal;word-break:keep-all}html body table th{font-weight:bold;color:#000}html body table td,html body table th{border:1px solid #d6d6d6;padding:6px 13px}html body dl{padding:0}html body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:bold}html body dl dd{padding:0 16px;margin-bottom:16px}html body code{font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:.85em !important;color:#000;background-color:#f0f0f0;border-radius:3px;padding:.2em 0}html body code::before,html body code::after{letter-spacing:-0.2em;content:"\00a0"}html body pre>code{padding:0;margin:0;font-size:.85em !important;word-break:normal;white-space:pre;background:transparent;border:0}html body .highlight{margin-bottom:16px}html body .highlight pre,html body pre{padding:1em;overflow:auto;font-size:.85em !important;line-height:1.45;border:#d6d6d6;border-radius:3px}html body .highlight pre{margin-bottom:0;word-break:normal}html body pre code,html body pre tt{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}html body pre code:before,html body pre tt:before,html body pre code:after,html body pre tt:after{content:normal}html body p,html body blockquote,html body ul,html body ol,html body dl,html body pre{margin-top:0;margin-bottom:16px}html body kbd{color:#000;border:1px solid #d6d6d6;border-bottom:2px solid #c7c7c7;padding:2px 4px;background-color:#f0f0f0;border-radius:3px}@media print{html body{background-color:#fff}html body h1,html body h2,html body h3,html body h4,html body h5,html body h6{color:#000;page-break-after:avoid}html body blockquote{color:#5c5c5c}html body pre{page-break-inside:avoid}html body table{display:table}html body img{display:block;max-width:100%;max-height:100%}html body pre,html body code{word-wrap:break-word;white-space:pre}}.markdown-preview{width:100%;height:100%;box-sizing:border-box}.markdown-preview .pagebreak,.markdown-preview .newpage{page-break-before:always}.markdown-preview pre.line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}.markdown-preview pre.line-numbers>code{position:relative}.markdown-preview pre.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:1em;font-size:100%;left:0;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.markdown-preview pre.line-numbers .line-numbers-rows>span{pointer-events:none;display:block;counter-increment:linenumber}.markdown-preview pre.line-numbers .line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}.markdown-preview .mathjax-exps .MathJax_Display{text-align:center !important}.markdown-preview:not([for="preview"]) .code-chunk .btn-group{display:none}.markdown-preview:not([for="preview"]) .code-chunk .status{display:none}.markdown-preview:not([for="preview"]) .code-chunk .output-div{margin-bottom:16px}.scrollbar-style::-webkit-scrollbar{width:8px}.scrollbar-style::-webkit-scrollbar-track{border-radius:10px;background-color:transparent}.scrollbar-style::-webkit-scrollbar-thumb{border-radius:5px;background-color:rgba(150,150,150,0.66);border:4px solid rgba(150,150,150,0.66);background-clip:content-box}html body[for="html-export"]:not([data-presentation-mode]){position:relative;width:100%;height:100%;top:0;left:0;margin:0;padding:0;overflow:auto}html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview{position:relative;top:0}@media screen and (min-width:914px){html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview{padding:2em calc(50% - 457px + 2em)}}@media screen and (max-width:914px){html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview{padding:2em}}@media screen and (max-width:450px){html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview{font-size:14px !important;padding:1em}}@media print{html body[for="html-export"]:not([data-presentation-mode]) #sidebar-toc-btn{display:none}}html body[for="html-export"]:not([data-presentation-mode]) #sidebar-toc-btn{position:fixed;bottom:8px;left:8px;font-size:28px;cursor:pointer;color:inherit;z-index:99;width:32px;text-align:center;opacity:.4}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] #sidebar-toc-btn{opacity:1}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc{position:fixed;top:0;left:0;width:300px;height:100%;padding:32px 0 48px 0;font-size:14px;box-shadow:0 0 4px rgba(150,150,150,0.33);box-sizing:border-box;overflow:auto;background-color:inherit}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc::-webkit-scrollbar{width:8px}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc::-webkit-scrollbar-track{border-radius:10px;background-color:transparent}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc::-webkit-scrollbar-thumb{border-radius:5px;background-color:rgba(150,150,150,0.66);border:4px solid rgba(150,150,150,0.66);background-clip:content-box}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc a{text-decoration:none}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc ul{padding:0 1.6em;margin-top:.8em}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc li{margin-bottom:.8em}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc ul{list-style-type:none}html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview{left:300px;width:calc(100% - 300px);padding:2em calc(50% - 457px - 150px);margin:0;box-sizing:border-box}@media screen and (max-width:1274px){html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview{padding:2em}}@media screen and (max-width:450px){html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview{width:100%}}html body[for="html-export"]:not([data-presentation-mode]):not([html-show-sidebar-toc]) .markdown-preview{left:50%;transform:translateX(-50%)}html body[for="html-export"]:not([data-presentation-mode]):not([html-show-sidebar-toc]) .md-sidebar-toc{display:none}
/* Please visit the URL below for more information: */
/* https://shd101wyy.github.io/markdown-preview-enhanced/#/customize-css */
</style>
</head>
<body for="html-export">
<div class="mume markdown-preview ">
<h1 class="mume-header" id="whitebox-token-authenticatie-in-topicus-hap">Whitebox token authenticatie in Topicus HAP</h1>
<p>Om de integratie van Whitebox token authenticatie in Topicus HAP mogelijk te maken hebben wij een manier bedacht om de huidige token authenticatie ook praktisch te kunnen gebruiken binnen een webapplicatie draaiende in een browser. Hierin worden de Whitebox authenticatie tokens ondertekend middels ZorgID. Om de inspanning van het implementatie traject aan de kant van Topicus HAP te beperken hebben wij een voorbeeld implementatie geschreven in Kotlin. Deze kan gedeeltelijk gebruikt worden as is, of als voorbeeld dienen voor de daadwerkelijke implementatie.</p>
<h2 class="mume-header" id="onderdelen">Onderdelen</h2>
<p>Dit project bevat twee onderdelen</p>
<ol>
<li>WBXAuthToken class (WBXAuthtoken.kt)</li>
</ol>
<p>De WBXAuthToken class representeert een authenticatie token zoals de Whitebox deze ondersteund.<br>
De class stelt methodes beschikbaar om een authenticatie token te coderen tot een formaat dat de ZorgID SDK kan ondertekenen en een token in het juiste formaat (JWS) kan formateren zodat een Whitebox deze kan gebruiken/valideren.</p>
<ol start="2">
<li>Voorbeeld applicatie (Main.kt)</li>
</ol>
<p>De voorbeeld applicatie demonstreert het proces van het genereren en verwerken van een authenticatie token.</p>
<h2 class="mume-header" id="wbxauthtoken-class-api">WBXAuthToken class API</h2>
<p>De constructor van de WBXAuthToken class neemt 3 argumenten: de Whitebox URL, de bijbehorende challenge en het UZI certificaat waarmee het token ondertekend gaat worden.</p>
<p>Daarnaast heeft de WBXAuthToken class 6 publieke methodes die het proces vormgeven.</p>
<p><strong>1) updateTimestamp</strong></p>
<p>Met deze methode wordt de timestamp van het token (de timestamp bepaald de uiteindelijke geldigheid van het token) naar de huidige datum en tijd ingesteld. Deze methode wordt aangeroepen bij het initialiseren van het token. Het is daarom niet nodig om deze zelf aan te roepen. Het is echter wel mogelijk (en aan te raden) om de timestamp te updaten indien er veel tijd zit tussen het initialiseren van het token en het daadwerkelijk ondertekenen van het token. <strong>Let op!</strong> als de data voor ondertekening is opgehaald met de getHashBag methode moet de updateTimestamp methode niet meer worden aangeroepen. De timestamp is onderdeel van de data die wordt ondertekend dus als deze methode wordt aangeroepen na ondertekening wordt de uiteindelijk gegenereerde JWS ongeldig. Dit wordt momenteel niet geforceerd in de class zelf.</p>
<p><strong>2) getHashBag</strong></p>
<p>Deze methode geeft de data terug die ondertekend moet worden, gewrapped in een <em>SignDataRequestBag</em>. Deze <em>SignDataRequestBag</em> kan aan de sign methode van de transaction manager (onderdeel van de ZorgID SDK) worden meegeven om te worden ondertekend.</p>
<p><strong>3) convertAndSetSignature</strong></p>
<p>Deze methode converteert de handtekening naar het juiste formaat en slaat deze op voor later gebruik.<br>
De handtekening die terug gegeven wordt vanuit de ZorgID SDK is gecodeerd in standaard base64 formaat. In de standaard die de Whitebox authenicatie token implementeerd (<a href="https://datatracker.ietf.org/doc/html/rfc7515">JWS</a>) wordt een URL base64 encoding scheme gebruikt zonder padding.</p>
<p><strong>4) jws</strong></p>
<p>Deze methode geeft het Whitebox authenticatie token terug in het <a href="https://datatracker.ietf.org/doc/html/rfc7515#section-3.1">JWS compact formaat</a>. Dit is het formaat dat de Whitebox verwacht.</p>
<p><strong>5) url</strong></p>
<p>Deze methode geeft de Whitebox URL terug waarvoor deze Whitebox authenticatie token toegang geeft.</p>
<p><strong>6) endpoint</strong></p>
<p>Deze methode geeft het endpoint (URL) terug waarnaar het token en url moeten worden opgestuurd (d.m.v een HTTP POST request).</p>
<h2 class="mume-header" id="whitebox-token-authenticatie-proces">Whitebox token authenticatie proces</h2>
<p>Naast de TLS sessie authenticatie, die momenteel gebruikt wordt door Topicus HAP, biedt de Whitebox ook een token authenticatie methode aan. Deze methode is op een relatief simple manier te implementeren binnen Topicus HAP met behulp van de ZorgID SDK/applicatie en de WBXAuthToken class.</p>
<p>Om het proces te laten werken is er naast alleen de Whitebox URL (zoals in de huidige implementatie) ook een zogenaamde challenge nodig. Deze wordt verkregen bij het opvragen van de Whitebox URL bij de Hapbox. Het betreffende veld is toegevoegd aan de search-and-copy call, die nu de volgende signature heeft:</p>
<p>Request:</p>
<pre data-role="codeBlock" data-info class="language-"><code>POST /haplink/topicus/0.2/search-and-copy
{"bsn": "123443210"}
</code></pre><p>Response:</p>
<pre data-role="codeBlock" data-info class="language-"><code>200 Ok
{
"url": "https://vrbld-amsterdam.mcs-net.nl/a6516we98r4w9e8/HL7v3-PS",
"challenge": "4257+pGn6EDeoiHYGKckFQ"
}
</code></pre><p>De challenge wordt samen met de URL en UZI pas gegevens ondertekend volgens de (<a href="https://datatracker.ietf.org/doc/html/rfc7515">JWS standaard</a>).</p>
<p>Het proces is als volgt:</p>
<ol>
<li>Initialiseer een WBXAuthToken met de URL, challenge en UZI certificaat van de huidige sessie</li>
<li>Roep de getHashBag methode aan</li>
<li>Roep de ITransactionManager.sign (ZorgID SDK) methode aan met de waarde die de getHashBag methode terug geeft</li>
<li>Wacht op de callback van de transaction manager (ZorgID SDK)</li>
<li>Extraheer de ondertekende data (eventArgs.signedDataBag.content[0].data)</li>
<li>Roep de convertAndSetSignature methode aan met de ondertekende data</li>
</ol>
<p>Nu is het WBXAuthToken gereed om een dossier bij de Whitebox op te halen.</p>
<p>Om het dossier op te halen moet er een HTTP POST request wordt gedaan vanuit een iFrame met de volgende velden:</p>
<pre data-role="codeBlock" data-info class="language-"><code>url=[de Whitebox url]
x-access-token=[de jws]
</code></pre><p>Voorbeeld van de content die in het iFrame moet worden geladen:</p>
<pre data-role="codeBlock" data-info class="language-"><code> <html>
<head></head>
<body>
<form method='POST' action='${wbxAuthToken.endpoint()}'>
<input type='hidden' name='x-access-token' value='${wbxAuthToken.jws()}'>
<input type='hidden' name='url' value='${wbxAuthToken.url()}'>
</form>
<script>document.querySelector('form').submit()</script>
</body>
</html>
</code></pre><p>Er zijn verschillende manieren denkbaar om bovenstaande HTML in een iframe te krijgen:</p>
<ol>
<li>genereer een data url van de inhoud van het iframe en geeft deze mee als het <em>src</em> attribuut</li>
<li>geef de inhoud van de iFrame mee het <em>srcdoc</em> attribuut</li>
<li>render de inhoud van het iFrame op een specifiek endpoint binnen Topicus HAP en geeft dit op als het <em>src</em> attribuut van het iFrame</li>
</ol>
<p>Methode 1 en 2 werken alleen als de CSP van de pagina waarop de iFrame wordt geladen dit toelaat.</p>
<p>De pagina die wordt geladen na het POST verzoek zal, net als in de huidige implementatie, een message posten op zijn parent window met daarin de hoogte van de content zodat de parent window daarop het iFrame kan resizen.</p>
<p>Voorbeeld message</p>
<pre data-role="codeBlock" data-info class="language-"><code>{
'height': 500
}
</code></pre>
</div>
</body></html>