Skip to content
Snippets Groups Projects

fix(view): open new views as tabs + show Getting Started button by default

1 file
+ 26
26
Compare changes
  • Side-by-side
  • Inline
@@ -11,7 +11,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
private waitDuration: WaitDuration;
private accessToken: string;
private refreshToken: string;
private deviceAuthorizationRequestResponseData: DeviceAuthorizationRequestResponseData;
private deviceAuthorizationRequestResponse: DeviceAuthorizationRequestResponse;
constructor(private deviceUrl: string, private tokenUrl: string, private userInfoUrl: string) {
/*
@@ -25,19 +25,15 @@ export default class KeycloakOAuth2DeviceFlowConnection {
this.waitDuration = new WaitDuration([5_000, 5_000, 5_000, 10_000, 10_000, 10_000, 30_000, 30_000, 100_000]);
this.accessToken = '';
this.refreshToken = '';
this.deviceAuthorizationRequestResponseData = {};
this.deviceAuthorizationRequestResponse = {};
}
/**
* userInfoUrl must be passed in constructor in order to use this
* @param accessToken
* @returns Promise
*/
public async tokenIsValid(accessToken: string) {
const url = this.userInfoUrl;
if (!url) {
return Promise.reject('tokenIsValid: missing user_info API endpoint');
}
if (!accessToken) {
return Promise.resolve(false);
}
@@ -56,15 +52,16 @@ export default class KeycloakOAuth2DeviceFlowConnection {
return Promise.resolve(true);
}
case HttpStatusCode.BAD_REQUEST: {
const badRequestResponse: FailedAuthenticationReponseData =
(await tokenValidationRequestResponse.json()) as FailedAuthenticationReponseData;
throw new Error('tokenIsValid: ' + badRequestResponse.error);
const badRequestResponse = (await tokenValidationRequestResponse.json()) as FailedAuthenticationResponse;
err(`${badRequestResponse.error!}: ${badRequestResponse.error_description}`);
throw new Error(`${badRequestResponse.error}: ${badRequestResponse.error_description}`);
}
case HttpStatusCode.UNAUTHORIZED: {
return Promise.resolve(false);
}
default: {
throw new Error(`tokenIsValid: Unhandled HTTP status: ${tokenValidationRequestResponseCode}`);
err(`Unhandled HTTP status: ${tokenValidationRequestResponseCode}`);
throw new Error(`Unhandled HTTP status: ${tokenValidationRequestResponseCode}`);
}
}
}
@@ -89,13 +86,13 @@ export default class KeycloakOAuth2DeviceFlowConnection {
await this.registerDevice();
}
try {
openLink(this.deviceAuthorizationRequestResponseData.verification_uri_complete!);
openLink(this.deviceAuthorizationRequestResponse.verification_uri_complete!);
await this.createUserAuthentication({
url: this.tokenUrl,
body: (() => {
const params = new URLSearchParams();
params.append('response_type', 'token');
params.append('device_code', this.deviceAuthorizationRequestResponseData.device_code ?? '');
params.append('device_code', this.deviceAuthorizationRequestResponse.device_code ?? '');
params.append('grant_type', 'urn:ietf:params:oauth:grant-type:device_code');
params.append('client_id', 'deadlock-desktop');
return params.toString();
@@ -108,7 +105,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
}
private deviceIsRegistered(): boolean {
return !!this.deviceAuthorizationRequestResponseData.device_code;
return !!this.deviceAuthorizationRequestResponse.device_code;
}
public async registerDevice() {
@@ -121,8 +118,8 @@ export default class KeycloakOAuth2DeviceFlowConnection {
return params.toString();
})(),
});
this.deviceAuthorizationRequestResponseData =
(await deviceAuthorizationRequestResponse.json()) as DeviceAuthorizationRequestResponseData;
this.deviceAuthorizationRequestResponse =
(await deviceAuthorizationRequestResponse.json()) as DeviceAuthorizationRequestResponse;
}
private async createDeviceAuthorization(args: { url: string; body: string }): Promise<Response> {
@@ -141,7 +138,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
/**
*
* @param args API URL endpoint to ask for a new token & request form parameters
* @throw Error containing Keycloak API error_code
* @throw Error containing Keycloak API `error_code`
*/
private async createUserAuthentication(args: { url: string; body: string }) {
const { url, body } = args;
@@ -159,7 +156,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
userAuthenticationRequestResponseCode = userAuthenticationRequestResponse.status;
switch (userAuthenticationRequestResponseCode) {
case HttpStatusCode.BAD_REQUEST: {
await this.onUserAuthenticationFailure(userAuthenticationRequestResponse);
await this.onUserAuthenticationBadRequest(userAuthenticationRequestResponse);
break;
}
@@ -169,14 +166,15 @@ export default class KeycloakOAuth2DeviceFlowConnection {
}
default: {
throw new Error(`tokenIsValid: Unhandled HTTP status: ${userAuthenticationRequestResponseCode}`);
err(`Unhandled HTTP status: ${userAuthenticationRequestResponseCode}`);
throw new Error(`Unhandled HTTP status: ${userAuthenticationRequestResponseCode}`);
}
}
}
}
private async onUserAuthenticationFailure(userAuthenticationRequestResponse: Response) {
const badRequestResponse = (await userAuthenticationRequestResponse.json()) as FailedAuthenticationReponseData;
private async onUserAuthenticationBadRequest(userAuthenticationRequestResponse: Response) {
const badRequestResponse = (await userAuthenticationRequestResponse.json()) as FailedAuthenticationResponse;
const errorCode = TokenFetchErrorCode[badRequestResponse.error!];
switch (errorCode) {
case TokenFetchErrorCode.invalid_client:
@@ -195,14 +193,16 @@ export default class KeycloakOAuth2DeviceFlowConnection {
break;
}
default: {
throw new Error(`createUserAuthentication: Unhandled error code [ ${badRequestResponse.error} ]`);
err(`${badRequestResponse.error!}: ${badRequestResponse.error_description}`);
throw new Error(
`Unhandled error code [ ${badRequestResponse.error!}: ${badRequestResponse.error_description} ]`,
);
}
}
}
private async onUserAuthenticationSuccess(userAuthenticationRequestResponse: Response) {
const successRequestResponse =
(await userAuthenticationRequestResponse.json()) as SuccessfulAuthenticationResponseData;
const successRequestResponse = (await userAuthenticationRequestResponse.json()) as SuccessfulAuthenticationResponse;
this.accessToken = successRequestResponse.access_token ?? '';
this.refreshToken = successRequestResponse.refresh_token ?? '';
}
@@ -212,7 +212,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
* KEEP the SAME case \
* to respect keycloak API return
*/
interface DeviceAuthorizationRequestResponseData {
interface DeviceAuthorizationRequestResponse {
device_code?: string;
user_code?: string;
verification_uri?: string;
@@ -225,7 +225,7 @@ interface DeviceAuthorizationRequestResponseData {
* KEEP the SAME case \
* to respect keycloak API return
*/
interface SuccessfulAuthenticationResponseData {
interface SuccessfulAuthenticationResponse {
access_token?: string;
expires_in?: number;
'not-before-policy'?: number;
@@ -236,7 +236,7 @@ interface SuccessfulAuthenticationResponseData {
token_type?: 'Bearer' | string;
}
interface FailedAuthenticationReponseData {
interface FailedAuthenticationResponse {
error?: string;
error_description?: string;
}
Loading