Skip to content

Commit

Permalink
Update response types and make fields consistent with other client sd…
Browse files Browse the repository at this point in the history
…ks (#77)
  • Loading branch information
chrisfisher authored Oct 17, 2024
1 parent ad890f8 commit 607adbb
Show file tree
Hide file tree
Showing 15 changed files with 181 additions and 109 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@authsignal/browser",
"version": "0.6.2",
"version": "1.0.0-alpha1",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
Expand Down
14 changes: 7 additions & 7 deletions src/api/email-api-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {buildHeaders, handleTokenExpired} from "./helpers";
import {ApiClientOptions, AuthsignalResponse, ChallengeResponse, EnrollResponse, VerifyResponse} from "./types/shared";
import {ApiClientOptions, ChallengeResponse, EnrollResponse, ErrorResponse, VerifyResponse} from "./types/shared";

export class EmailApiClient {
tenantId: string;
Expand All @@ -12,7 +12,7 @@ export class EmailApiClient {
this.onTokenExpired = onTokenExpired;
}

async enroll({token, email}: {token: string; email: string}) {
async enroll({token, email}: {token: string; email: string}): Promise<EnrollResponse | ErrorResponse> {
const body = {email};

const response = await fetch(`${this.baseUrl}/client/user-authenticators/email-otp`, {
Expand All @@ -21,27 +21,27 @@ export class EmailApiClient {
body: JSON.stringify(body),
});

const responseJson: AuthsignalResponse<EnrollResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

return responseJson;
}

async challenge({token}: {token: string}) {
async challenge({token}: {token: string}): Promise<ChallengeResponse | ErrorResponse> {
const response = await fetch(`${this.baseUrl}/client/challenge/email-otp`, {
method: "POST",
headers: buildHeaders({token, tenantId: this.tenantId}),
});

const responseJson: AuthsignalResponse<ChallengeResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

return responseJson;
}

async verify({token, code}: {token: string; code: string}) {
async verify({token, code}: {token: string; code: string}): Promise<VerifyResponse | ErrorResponse> {
const body = {verificationCode: code};

const response = await fetch(`${this.baseUrl}/client/verify/email-otp`, {
Expand All @@ -50,7 +50,7 @@ export class EmailApiClient {
body: JSON.stringify(body),
});

const responseJson: AuthsignalResponse<VerifyResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

Expand Down
4 changes: 2 additions & 2 deletions src/api/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {AuthsignalResponse} from "./types/shared";
import {ErrorResponse} from "./types/shared";

type BuildHeadersParams = {
token?: string;
Expand All @@ -15,7 +15,7 @@ export function buildHeaders({token, tenantId}: BuildHeadersParams) {
}

type HandleTokenExpiredParams<T> = {
response: AuthsignalResponse<T>;
response: T | ErrorResponse;
onTokenExpired?: () => void;
};

Expand Down
41 changes: 29 additions & 12 deletions src/api/passkey-api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import {
AddAuthenticatorResponse,
AuthenticationOptsRequest,
AuthenticationOptsResponse,
ErrorResponse,
PasskeyAuthenticatorResponse,
RegistrationOptsRequest,
RegistrationOptsResponse,
VerifyRequest,
VerifyResponse,
} from "./types/passkey";
import {ApiClientOptions, AuthsignalResponse, ChallengeResponse} from "./types/shared";
import {ApiClientOptions, ChallengeResponse} from "./types/shared";

export class PasskeyApiClient {
tenantId: string;
Expand All @@ -23,7 +24,11 @@ export class PasskeyApiClient {
this.onTokenExpired = onTokenExpired;
}

async registrationOptions({token, username, authenticatorAttachment}: {token: string} & RegistrationOptsRequest) {
async registrationOptions({
token,
username,
authenticatorAttachment,
}: {token: string} & RegistrationOptsRequest): Promise<RegistrationOptsResponse | ErrorResponse> {
const body: RegistrationOptsRequest = Boolean(authenticatorAttachment)
? {username, authenticatorAttachment}
: {username};
Expand All @@ -34,14 +39,17 @@ export class PasskeyApiClient {
body: JSON.stringify(body),
});

const responseJson: AuthsignalResponse<RegistrationOptsResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

return responseJson;
}

async authenticationOptions({token, challengeId}: {token?: string} & AuthenticationOptsRequest) {
async authenticationOptions({
token,
challengeId,
}: {token?: string} & AuthenticationOptsRequest): Promise<AuthenticationOptsResponse | ErrorResponse> {
const body: AuthenticationOptsRequest = {challengeId};

const response = await fetch(`${this.baseUrl}/client/user-authenticators/passkey/authentication-options`, {
Expand All @@ -50,14 +58,18 @@ export class PasskeyApiClient {
body: JSON.stringify(body),
});

const responseJson: AuthsignalResponse<AuthenticationOptsResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

return responseJson;
}

async addAuthenticator({token, challengeId, registrationCredential}: {token: string} & AddAuthenticatorRequest) {
async addAuthenticator({
token,
challengeId,
registrationCredential,
}: {token: string} & AddAuthenticatorRequest): Promise<AddAuthenticatorResponse | ErrorResponse> {
const body: AddAuthenticatorRequest = {
challengeId,
registrationCredential,
Expand All @@ -69,14 +81,19 @@ export class PasskeyApiClient {
body: JSON.stringify(body),
});

const responseJson: AuthsignalResponse<AddAuthenticatorResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

return responseJson;
}

async verify({token, challengeId, authenticationCredential, deviceId}: {token?: string} & VerifyRequest) {
async verify({
token,
challengeId,
authenticationCredential,
deviceId,
}: {token?: string} & VerifyRequest): Promise<VerifyResponse | ErrorResponse> {
const body: VerifyRequest = {challengeId, authenticationCredential, deviceId};

const response = await fetch(`${this.baseUrl}/client/verify/passkey`, {
Expand All @@ -85,7 +102,7 @@ export class PasskeyApiClient {
body: JSON.stringify(body),
});

const responseJson: AuthsignalResponse<VerifyResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

Expand All @@ -96,7 +113,7 @@ export class PasskeyApiClient {
credentialIds,
}: {
credentialIds: string[];
}): Promise<AuthsignalResponse<PasskeyAuthenticatorResponse>> {
}): Promise<PasskeyAuthenticatorResponse | ErrorResponse> {
const response = await fetch(`${this.baseUrl}/client/user-authenticators/passkey?credentialIds=${credentialIds}`, {
method: "GET",
headers: buildHeaders({tenantId: this.tenantId}),
Expand All @@ -109,14 +126,14 @@ export class PasskeyApiClient {
return response.json();
}

async challenge(action: string) {
async challenge(action: string): Promise<ChallengeResponse | ErrorResponse> {
const response = await fetch(`${this.baseUrl}/client/challenge`, {
method: "POST",
headers: buildHeaders({tenantId: this.tenantId}),
body: JSON.stringify({action}),
});

const responseJson: AuthsignalResponse<ChallengeResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

Expand Down
14 changes: 7 additions & 7 deletions src/api/sms-api-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {buildHeaders, handleTokenExpired} from "./helpers";
import {ApiClientOptions, AuthsignalResponse, ChallengeResponse, EnrollResponse, VerifyResponse} from "./types/shared";
import {ApiClientOptions, ChallengeResponse, EnrollResponse, ErrorResponse, VerifyResponse} from "./types/shared";

export class SmsApiClient {
tenantId: string;
Expand All @@ -12,7 +12,7 @@ export class SmsApiClient {
this.onTokenExpired = onTokenExpired;
}

async enroll({token, phoneNumber}: {token: string; phoneNumber: string}) {
async enroll({token, phoneNumber}: {token: string; phoneNumber: string}): Promise<EnrollResponse | ErrorResponse> {
const body = {phoneNumber};

const response = await fetch(`${this.baseUrl}/client/user-authenticators/sms`, {
Expand All @@ -21,27 +21,27 @@ export class SmsApiClient {
body: JSON.stringify(body),
});

const responseJson: AuthsignalResponse<EnrollResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

return responseJson;
}

async challenge({token}: {token: string}) {
async challenge({token}: {token: string}): Promise<ChallengeResponse | ErrorResponse> {
const response = await fetch(`${this.baseUrl}/client/challenge/sms`, {
method: "POST",
headers: buildHeaders({token, tenantId: this.tenantId}),
});

const responseJson: AuthsignalResponse<ChallengeResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

return responseJson;
}

async verify({token, code}: {token: string; code: string}) {
async verify({token, code}: {token: string; code: string}): Promise<VerifyResponse | ErrorResponse> {
const body = {verificationCode: code};

const response = await fetch(`${this.baseUrl}/client/verify/sms`, {
Expand All @@ -50,7 +50,7 @@ export class SmsApiClient {
body: JSON.stringify(body),
});

const responseJson: AuthsignalResponse<VerifyResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

Expand Down
12 changes: 6 additions & 6 deletions src/api/totp-api-client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {buildHeaders, handleTokenExpired} from "./helpers";
import {ApiClientOptions, AuthsignalResponse, VerifyResponse} from "./types/shared";
import {EnrollResponse} from "./types/totp";
import {ApiClientOptions, ErrorResponse, VerifyResponse} from "./types/shared";
import {EnrollTotpResponse} from "./types/totp";

export class TotpApiClient {
tenantId: string;
Expand All @@ -13,20 +13,20 @@ export class TotpApiClient {
this.onTokenExpired = onTokenExpired;
}

async enroll({token}: {token: string}) {
async enroll({token}: {token: string}): Promise<EnrollTotpResponse | ErrorResponse> {
const response = await fetch(`${this.baseUrl}/client/user-authenticators/totp`, {
method: "POST",
headers: buildHeaders({token, tenantId: this.tenantId}),
});

const responseJson: AuthsignalResponse<EnrollResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

return responseJson;
}

async verify({token, code}: {token: string; code: string}) {
async verify({token, code}: {token: string; code: string}): Promise<VerifyResponse | ErrorResponse> {
const body = {verificationCode: code};

const response = await fetch(`${this.baseUrl}/client/verify/totp`, {
Expand All @@ -35,7 +35,7 @@ export class TotpApiClient {
body: JSON.stringify(body),
});

const responseJson: AuthsignalResponse<VerifyResponse> = await response.json();
const responseJson = await response.json();

handleTokenExpired({response: responseJson, onTokenExpired: this.onTokenExpired});

Expand Down
9 changes: 9 additions & 0 deletions src/api/types/passkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,12 @@ export type PasskeyAuthenticatorResponse = {
credentialId: string;
verifiedAt: string;
};

export type ChallengeResponse = {
challengeId: string;
};

export type ErrorResponse = {
error: string;
errorDescription?: string;
};
2 changes: 0 additions & 2 deletions src/api/types/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,3 @@ export type ErrorResponse = {
errorCode?: "expired_token" | (string & {});
errorDescription?: string;
};

export type AuthsignalResponse<T> = T | ErrorResponse;
2 changes: 1 addition & 1 deletion src/api/types/totp.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type EnrollResponse = {
export type EnrollTotpResponse = {
userAuthenticatorId: string;
userId: string;
uri: string;
Expand Down
25 changes: 16 additions & 9 deletions src/email.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {EmailApiClient} from "./api/email-api-client";
import {ChallengeResponse, EnrollResponse, VerifyResponse} from "./api/types/shared";
import {handleApiResponse} from "./helpers";
import {TokenCache} from "./token-cache";
import {AuthsignalResponse} from "./types";

type EmailOptions = {
baseUrl: string;
Expand All @@ -23,33 +26,37 @@ export class Email {
this.api = new EmailApiClient({baseUrl, tenantId, onTokenExpired});
}

async enroll({email}: EnrollParams) {
async enroll({email}: EnrollParams): Promise<AuthsignalResponse<EnrollResponse>> {
if (!this.cache.token) {
return this.cache.handleTokenNotSetError();
}

return this.api.enroll({token: this.cache.token, email});
const response = await this.api.enroll({token: this.cache.token, email});

return handleApiResponse(response);
}

async challenge() {
async challenge(): Promise<AuthsignalResponse<ChallengeResponse>> {
if (!this.cache.token) {
return this.cache.handleTokenNotSetError();
}

return this.api.challenge({token: this.cache.token});
const response = await this.api.challenge({token: this.cache.token});

return handleApiResponse(response);
}

async verify({code}: VerifyParams) {
async verify({code}: VerifyParams): Promise<AuthsignalResponse<VerifyResponse>> {
if (!this.cache.token) {
return this.cache.handleTokenNotSetError();
}

const verifyResponse = await this.api.verify({token: this.cache.token, code});
const response = await this.api.verify({token: this.cache.token, code});

if ("accessToken" in verifyResponse && verifyResponse.accessToken) {
this.cache.token = verifyResponse.accessToken;
if ("accessToken" in response && response.accessToken) {
this.cache.token = response.accessToken;
}

return verifyResponse;
return handleApiResponse(response);
}
}
Loading

0 comments on commit 607adbb

Please sign in to comment.