Skip to content

Commit

Permalink
[SME-912] support business token flow (#30)
Browse files Browse the repository at this point in the history
This is related to curity [PR
here](TransferGo/curity-server#408) and
[qa-tests PR](TransferGo/qa-test#1043)

Support new authentication actions.
Modified login screen to allow selecting which scopes to request after
authentication.
  • Loading branch information
grantas-valiukenas-tg authored Jul 16, 2024
1 parent a720037 commit 62f7241
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 34 deletions.
36 changes: 27 additions & 9 deletions src/components/Authenticated.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function Authenticated(props) {
return decodeToken(idToken).sub;
};

const { id_token, access_token, scope, expires_in } = props.tokens;
const { id_token, access_token, expires_in, nonce_token, refresh_token } = props.tokens;

localStorage.setItem('idToken', id_token);

Expand All @@ -54,30 +54,48 @@ export default function Authenticated(props) {
style={{ maxWidth: "300px" }}
alt="Curity HAAPI React Demo"
/>

<h3>Access Token</h3>
<pre id="access-token-container" className="json-container">{access_token}</pre>
<h3>Scopes</h3>
<pre id="scopes-container" className="json-container">{scope}</pre>
<h3>Access token expires in (seconds)</h3>

<h3>Access Token Claims</h3>
<pre
className="json-container"
dangerouslySetInnerHTML={{
__html: prettyPrintJson.toHtml(decodeToken(access_token)),
}}
/>

<h3>Access token expires in (seconds)</h3>
<pre id="token-expire-container" className="json-container">{expires_in}</pre>

<h3>ID Token claims</h3>
<h3>refresh_token, nonce_token</h3>
<pre className="json-container">{refresh_token} {nonce_token}</pre>

<pre
hidden
id="accessTokenClaims"
className="json-container"
dangerouslySetInnerHTML={{
__html: prettyPrintJson.toHtml(decodeToken(id_token)),
}}
__html: JSON.stringify(decodeToken(access_token)),
}}
/>
<h3>ID Token claims as a string</h3>
<pre
hidden
id="tokenClaims"
id="idTokenClaims"
className="json-container"
dangerouslySetInnerHTML={{
__html: JSON.stringify(decodeToken(id_token)),
}}
/>
<pre
hidden
id="nonceToken"
className="json-container"
dangerouslySetInnerHTML={{
__html: nonce_token,
}}
/>
</div>
</Well>
</Page>
Expand Down
8 changes: 5 additions & 3 deletions src/components/HAAPIProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ export default function HAAPIProcessor(props) {
case 'authentication-action/captcha-action/index':
case 'authenticator/otp-captcha/hcaptcha/get':
case 'authenticator/device-secret/authentication/get':
case 'authenticator/business-nonce/authentication/get':
case 'authenticator/phone-number-authenticator/authenticate/get':
case 'authentication-action/reset-password/index':
case 'authentication-action/create-passcode/index':
case 'authentication-action/attribute-prompt/index':
return <UsernamePassword
haapiResponse={haapiResponse}
submitForm={(formState, url, method) => submitForm(formState, url, method)}
Expand Down Expand Up @@ -132,11 +134,11 @@ export default function HAAPIProcessor(props) {
}
}

const startAuthorization = async () => {
const startAuthorization = async (scopes) => {
setStep({ name: 'loading', haapiResponse: null })
setIsLoading(true)

const url = await oidcClient.getAuthorizationUrl()
const url = await oidcClient.getAuthorizationUrl(scopes)
await callHaapi(url)
}

Expand Down Expand Up @@ -400,7 +402,7 @@ export default function HAAPIProcessor(props) {
<Heading title="This is a demo app showing HAAPI capabilities" />
<p>Click the button below to start the login flow without leaving this SPA</p>
</div>
<StartAuthorization startAuthorization={() => startAuthorization()} />
<StartAuthorization startAuthorization={startAuthorization} />
</Well>
</Page>
</Layout>
Expand Down
4 changes: 2 additions & 2 deletions src/components/OidcClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ class OidcClient {
setTokens(tokens)
}

getAuthorizationUrl = async () => {
getAuthorizationUrl = async (scopes) => {
const codeChallenge = await calculateCodeChallenge(this.codeVerifier)

const url = new URL(config.authorizationEndpoint)
const queryParams = url.searchParams
queryParams.append('client_id', config.clientId)
queryParams.append('scope', config.scope)
queryParams.append('scope', scopes)
queryParams.append('response_type', 'code')
queryParams.append('code_challenge_method', 'S256')
queryParams.append('code_challenge', codeChallenge)
Expand Down
70 changes: 51 additions & 19 deletions src/components/StartAuthorization.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,55 @@
/*
* Copyright 2022 Curity AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState } from "react";

export default function StartAuthorization(props) {
const { startAuthorization } = props
const { startAuthorization } = props;

return <>
<button onClick={startAuthorization} className="button button-primary button-fullwidth" id="submit" data-qa="start">Login</button>
</>
const [scopes, setScopes] = useState(new Set(["openid", "user"]));

const onChange = (name, checked) => {
if (checked) scopes.add(name);
else scopes.delete(name);
const newSet = new Set(Array.from(scopes));
setScopes(newSet);
};

return (
<div className="login-container">
<h3>Select scopes</h3>
<Checkbox
name="openid"
checked={scopes.has("openid")}
onChange={onChange}
/>
<Checkbox name="user" checked={scopes.has("user")} onChange={onChange} />
<Checkbox
name="business"
checked={scopes.has("business")}
onChange={onChange}
/>
<button
onClick={() => startAuthorization(Array.from(scopes).join(" "))}
className="button button-primary button-fullwidth"
id="submit"
data-qa="start"
>
Login
</button>
</div>
);
}

const Checkbox = ({ name, checked, onChange }) => {
return (
<div>
<label htmlFor={name} className="label">
{name}
</label>
<input
name={name}
type="checkbox"
checked={checked}
onChange={(event) => onChange(name, event.target.checked)}
/>
</div>
);
};
1 change: 0 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ if (url.has('authenticator')) {

let config = {
clientId: clientId,
scope: 'openid',
authenticator: authenticator,
};

Expand Down
11 changes: 11 additions & 0 deletions src/scss/curity-example-app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,14 @@
z-index: 10;
left: calc(50% - 100px);
}

.login-container{
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;

& label{
margin-right: 5px;
}
}

0 comments on commit 62f7241

Please sign in to comment.