Skip to content

Commit

Permalink
feat(ts): add client helper functions to SDK (#373)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-jonas authored Sep 18, 2024
1 parent b53c141 commit 6fb21b4
Show file tree
Hide file tree
Showing 20 changed files with 1,349 additions and 0 deletions.
7 changes: 7 additions & 0 deletions clients/client/typescript-fetch/.openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ src/apis/RelationshipApi.ts
src/apis/WellknownApi.ts
src/apis/WorkspaceApi.ts
src/apis/index.ts
src/contrib/continueWith.ts
src/contrib/error.ts
src/contrib/flowTypes.ts
src/contrib/index.ts
src/contrib/ui.ts
src/contrib/urlHelpers.ts
src/contrib/utils.ts
src/index.ts
src/models/AcceptOAuth2ConsentRequest.ts
src/models/AcceptOAuth2ConsentRequestSession.ts
Expand Down
146 changes: 146 additions & 0 deletions clients/client/typescript-fetch/src/contrib/continueWith.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0

import {
ContinueWith,
ContinueWithRecoveryUi,
ContinueWithSetOrySessionToken,
ContinueWithSettingsUi,
ContinueWithVerificationUi,
ContinueWithRedirectBrowserTo,
} from "../";

export type OnRedirectHandler = (url: string, external: boolean) => void;

// The order in which the actions are defined here is the order in which they are expected to be executed.
const continueWithPriority = [
"show_settings_ui",
"show_recovery_ui",
"show_verification_ui",
"redirect_browser_to",
"set_ory_session_token",
];

export function handleContinueWith(
continueWith: ContinueWith[] | undefined,
{ onRedirect }: { onRedirect: OnRedirectHandler }
): boolean {
if (!continueWith || continueWith.length === 0) {
return false;
}

const action = pickBestContinueWith(continueWith);
if (!action) {
return false;
}

const redirectFlow = (id: string, flow: string, url?: string) => {
if (url) {
onRedirect(url, true);
return true;
}

onRedirect("/" + flow + "?flow=" + id, false);
return true;
};

if (isSetOrySessionToken(action)) {
throw new Error("Ory Elements does not support API flows yet.");
} else if (isRedirectBrowserTo(action) && action.redirect_browser_to) {
// console.log("Redirecting to", action.redirect_browser_to)
onRedirect(action.redirect_browser_to, true);
return true;
} else if (isShowVerificationUi(action)) {
return redirectFlow(action.flow.id, "verification", action.flow.url);
} else if (isShowRecoveryUi(action)) {
return redirectFlow(action.flow.id, "recovery", action.flow.url);
} else if (isShowSettingsUi(action)) {
// TODO: re-add url
return redirectFlow(action.flow.id, "settings");
} else {
throw new Error("Unknown action: " + JSON.stringify(action));
}
}

/**
* Picks the best continue with action from the list of continue with actions.
*
* @param continueWith - The list of continue with actions.
*/
export function pickBestContinueWith(continueWith: ContinueWith[]) {
if (!continueWith || continueWith.length === 0) {
return;
}

const sorted = continueWith.sort(
(a, b) =>
continueWithPriority.indexOf(a.action) -
continueWithPriority.indexOf(b.action)
);
return sorted[0];
}

/**
* Checks if the continue with action is to set the Ory Session Token.
*
* @param continueWith - The continue with action.
*/
export function isSetOrySessionToken(
continueWith: ContinueWith
): continueWith is ContinueWithSetOrySessionToken & {
action: "set_ory_session_token";
} {
return continueWith.action === "set_ory_session_token";
}

/**
* Checks if the continue with action is to redirect the browser to a different page.
*
* @param continueWith - The continue with action.
*/
export function isRedirectBrowserTo(
continueWith: ContinueWith
): continueWith is ContinueWithRedirectBrowserTo & {
action: "redirect_browser_to";
} {
return continueWith.action === "redirect_browser_to";
}

/**
* Checks if the continue with action is to show the recovery UI.
*
* @param continueWith - The continue with action.
*/
export function isShowRecoveryUi(
continueWith: ContinueWith
): continueWith is ContinueWithRecoveryUi & {
action: "show_recovery_ui";
} {
return continueWith.action === "show_recovery_ui";
}

/**
* Checks if the continue with action is to show the settings UI.
*
* @param continueWith - The continue with action.
*/
export function isShowSettingsUi(
continueWith: ContinueWith
): continueWith is ContinueWithSettingsUi & {
action: "show_settings_ui";
} {
return continueWith.action === "show_settings_ui";
}

/**
* Checks if the continue with action is to show the verification UI.
*
* @param continueWith - The continue with action.
*/
export function isShowVerificationUi(
continueWith: ContinueWith
): continueWith is ContinueWithVerificationUi & {
action: "show_verification_ui";
} {
return continueWith.action === "show_verification_ui";
}
227 changes: 227 additions & 0 deletions clients/client/typescript-fetch/src/contrib/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0

import {
ErrorBrowserLocationChangeRequired,
ErrorFlowReplaced,
GenericError,
NeedsPrivilegedSessionError,
ResponseError,
SelfServiceFlowExpiredError,
} from "../";

export function isGenericErrorResponse(
response: unknown
): response is { error: GenericError } {
return (
typeof response === "object" &&
!!response &&
"error" in response &&
typeof response.error === "object" &&
!!response.error &&
"id" in response.error
);
}

/**
* Checks if the response is a NeedsPrivilegedSessionError. This error is returned when the self-service flow requires
* the user to re-authenticate in order to perform an action that requires elevated privileges.
*
* @param response - The response to check.
*/
export function isNeedsPrivilegedSessionError(
response: unknown
): response is NeedsPrivilegedSessionError {
return (
isGenericErrorResponse(response) &&
response.error.id === "session_refresh_required"
);
}

/**1
* Checks if the response is a SelfServiceFlowExpiredError. This error is returned when the self-service flow is expired.
*
* @param response - The response to check.
*/
export function isSelfServiceFlowExpiredError(
response: unknown
): response is SelfServiceFlowExpiredError {
return (
isGenericErrorResponse(response) &&
response.error.id === "self_service_flow_expired"
);
}

/**
* Checks if the response is a GenericError due to the self-service flow being disabled (for example disabled registration).
*
* @param response - The response to check.
*/
export function isSelfServiceFlowDisabled(
response: unknown
): response is GenericError {
return (
isGenericErrorResponse(response) &&
isGenericErrorResponse(response) &&
response.error.id === "self_service_flow_disabled"
);
}

/**
* Checks if the response is a ErrorBrowserLocationChangeRequired.
* @param response - The response to check.
*/
export function isBrowserLocationChangeRequired(
response: unknown
): response is ErrorBrowserLocationChangeRequired {
return (
isGenericErrorResponse(response) &&
isGenericErrorResponse(response) &&
response.error.id === "browser_location_change_required"
);
}

/**
* Checks if the response is a ErrorFlowReplaced.
* @param response - The response to check.
*/
export function isSelfServiceFlowReplaced(
response: unknown
): response is ErrorFlowReplaced {
return (
isGenericErrorResponse(response) &&
response.error.id === "self_service_flow_replaced"
);
}

/**
* Checks if the response is a GenericError due to the session already being available.
* @param response - The response to check.
*/
export function isSessionAlreadyAvailable(
response: unknown
): response is GenericError {
return (
isGenericErrorResponse(response) &&
response.error.id === "session_already_available"
);
}

/**
* Checks if the response is a GenericError due to the session being inactive.
*
* @param response - The response to check.
*/
export function isAddressNotVerified(
response: unknown
): response is GenericError {
return (
isGenericErrorResponse(response) &&
response.error.id === "session_verified_address_required"
);
}

/**
* Checks if the response is a GenericError due to the session already having fulfilled the AAL requirement.
*
* @param response - The response to check.
*/
export function isAalAlreadyFulfilled(
response: unknown
): response is GenericError {
return (
isGenericErrorResponse(response) &&
response.error.id === "session_aal_already_fulfilled"
);
}

/**
* Checks if the response is a GenericError due to the session requiring a higher AAL.
*
* @param response - The response to check.
*/
export function isSessionAal1Required(
response: unknown
): response is GenericError {
return (
isGenericErrorResponse(response) &&
response.error.id === "session_aal1_required"
);
}

/**
* Checks if the response is a GenericError due to the session requiring a higher AAL.
*
* @param response - The response to check.
*/
export function isSessionAal2Required(
response: unknown
): response is GenericError {
return (
isGenericErrorResponse(response) &&
response.error.id === "session_aal2_required"
);
}

/**
* Checks if the response is a GenericError due to the session being inactive.
*
* @param response - The response to check.
*/
export function isNoActiveSession(response: unknown): response is GenericError {
return (
isGenericErrorResponse(response) && response.error.id === "session_inactive"
);
}
/**
* Checks if the response is a GenericError due to a CSRF violation.
*
* @param response - The response to check.
*/
export function isCsrfError(response: unknown): response is GenericError {
return (
isGenericErrorResponse(response) &&
response.error.id === "security_csrf_violation"
);
}

/**
* Checks if the response is a GenericError due to the redirect URL being forbidden.
*
* @param response - The response to check.
*/
export function isRedirectUrlNotAllowed(
response: unknown
): response is GenericError {
return (
isGenericErrorResponse(response) &&
response.error.id === "self_service_flow_return_to_forbidden"
);
}

/**
* Checks if the response is a GenericError due to two sessions being active.
*
* @param response - The response to check.
*/
export function isSecurityIdentityMismatch(
response: unknown
): response is GenericError {
return (
isGenericErrorResponse(response) &&
response.error.id === "security_identity_mismatch"
);
}

export const isResponseError = (err: unknown): err is ResponseError => {
if (err instanceof ResponseError) {
return true;
}

return (
typeof err === "object" &&
!!err &&
"name" in err &&
err.name === "ResponseError"
);
};
Loading

0 comments on commit 6fb21b4

Please sign in to comment.