diff --git a/packages/authgear-core/src/error.ts b/packages/authgear-core/src/error.ts index 1faa6361..2528a77a 100644 --- a/packages/authgear-core/src/error.ts +++ b/packages/authgear-core/src/error.ts @@ -20,14 +20,50 @@ export class AuthgearError extends Error { * @public */ export enum ErrorName { + /** + * Indicates that the server does not understand the request (i.e. syntactic error). + * Status code: 400 + */ BadRequest = "BadRequest", + /** + * Indicates that the server understands the request, but refuse to process it (i.e. semantic error). + * Status code: 400 + */ Invalid = "Invalid", + /** + * Indicates that the client does not have valid credentials (i.e. authentication error). + * Status code: 401 + */ Unauthorized = "Unauthorized", + /** + * Indicates that the client's credentials are not allowed for the request (i.e. authorization error). + * Status code: 403 + */ Forbidden = "Forbidden", + /** + * Indicates that the server cannot find the requested resource. + * Status code: 404 + */ NotFound = "NotFound", + /** + * Indicates that the resource is already exists on the server. + * Status code: 409 + */ AlreadyExists = "AlreadyExists", + /** + * Indicates that the client has sent too many requests in a given amount of time. + * Status code: 429 + */ TooManyRequest = "TooManyRequest", + /** + * Indicates that the server encountered an unexpected condition and unable to process the request. + * Status code: 500 + */ InternalError = "InternalError", + /** + * Indicates that the server is not ready to handle the request. + * Status code: 503 + */ ServiceUnavailable = "ServiceUnavailable", } @@ -143,28 +179,31 @@ export function _decodeError(err: any): Error { } /** - * PreAuthenticatedURLNotAllowedError + * PreAuthenticatedURLNotAllowedError is the root class of errors related to pre-authenticated URL. * * @public */ export class PreAuthenticatedURLNotAllowedError extends AuthgearError {} /** - * PreAuthenticatedURLInsufficientScopeError + * This may happen if the "Pre-authenticated URL" feature was not enabled when the user logged in during this session. + * Ask the user to log in again to enable this feature. * * @public */ export class PreAuthenticatedURLInsufficientScopeError extends PreAuthenticatedURLNotAllowedError {} /** - * PreAuthenticatedURLIDTokenNotFoundError + * The user logged in from an older SDK version that does not support the pre-authenticated URL. + * Ask the user to log in again to resolve the problem. * * @public */ export class PreAuthenticatedURLIDTokenNotFoundError extends PreAuthenticatedURLNotAllowedError {} /** - * PreAuthenticatedURLDeviceSecretNotFoundError + * The device secret is not found. This may happen if the "Pre-authenticated URL" feature was not enabled when the user logged in during this session. + * Ask the user to log in again to enable this feature. * * @public */ diff --git a/packages/authgear-core/src/types.ts b/packages/authgear-core/src/types.ts index e2c6e96c..1fc1813b 100644 --- a/packages/authgear-core/src/types.ts +++ b/packages/authgear-core/src/types.ts @@ -1,7 +1,11 @@ /** * UserInfo is the result of fetchUserInfo. * It contains `sub` which is the User ID, - * as well OIDC standard claims like `email`. + * as well as OIDC standard claims like `email`, + * see https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims. + * + * In addition to these standard claims, it may include custom claims + * defined by Authgear to support additional functionality like `isVerified`. * * @public */ @@ -49,7 +53,13 @@ export interface UserInfo { * @public */ export enum ColorScheme { + /** + * Force to use the light color scheme in the AuthUI when the project config is "Auto". + */ Light = "light", + /** + * Force to use the dark color scheme in the AuthUI when the project config is "Auto". + */ Dark = "dark", } @@ -59,9 +69,28 @@ export enum ColorScheme { * @public */ export enum PromptOption { + /** + * The `none` prompt is used to sliently authenticate the user without prompting for any action. + * This prompt bypasses the need for `login` and `consent` prompts + * only when the user has previously given consent to the application and has an active session. + */ None = "none", + /** + * The `login` prompt requires the user to log in to the authentication provider which forces the user to re-authenticate. + */ Login = "login", + /** + * The `consent` prompt asks the user to consent to the scopes. + * + * @internal + */ Consent = "consent", + /** + * The select_account prompt present a "Continue" screen to for the user to choose + * to continue with the session in the cookies or login to another account. + * + * @internal + */ SelectAccount = "select_account", } @@ -212,8 +241,17 @@ export interface _AnonymousUserPromotionCodeResponse { * @public */ export interface TokenStorage { + /** + * Stores a refresh token for a give namespace to the storage. + */ setRefreshToken(namespace: string, refreshToken: string): Promise; + /** + * Retrieves the refresh token associated with a specific namespace in the storage. + */ getRefreshToken(namespace: string): Promise; + /** + * Deletes the refresh token for the specified namespace in the storage. + */ delRefreshToken(namespace: string): Promise; } @@ -268,6 +306,11 @@ export interface _StorageDriver { * @public */ export interface ContainerOptions { + /** + * The name of the container. The name is used as the namespace of `TokenStorage`. + * One use case is to use multiple containers with different names to support signing in multiple accounts. + * @defaultValue "default" + */ name?: string; } @@ -341,6 +384,8 @@ export interface _OIDCTokenResponse { * After a call to configure, the session state would become "AUTHENTICATED" if a previous session was found, * or "NO_SESSION" if such session was not found. * + * Please refer to {@link SessionStateChangeReason} for more information. + * * @public */ export enum SessionState { @@ -355,7 +400,7 @@ export enum SessionState { * These reasons can be thought of as the transition of a SessionState, which is described as follows: * * ``` - * LOGOUT / INVALID + * LOGOUT / INVALID / CLEAR * +----------------------------------------------+ * v | * State: UNKNOWN ----- NO_TOKEN ----> State: NO_SESSION ---- AUTHENTICATED -----> State: AUTHENTICATED @@ -375,18 +420,34 @@ export enum SessionStateChangeReason { } /** + * The path of the page in Authgear. + * * @public */ export enum Page { + /** + * The path of the settings page in Authgear. + */ Settings = "/settings", + /** + * The path of the indenties page in Authgear. + */ Identities = "/settings/identities", } /** + * The actions that can be performed in Authgear settings page. + * * @public */ export enum SettingsAction { + /** + * Change password in Authgear settings page. + */ ChangePassword = "change_password", + /** + * Delete account in Authgear settings page. + */ DeleteAccount = "delete_account", } diff --git a/packages/authgear-react-native/src/index.ts b/packages/authgear-react-native/src/index.ts index c7e22308..d38fa4b1 100644 --- a/packages/authgear-react-native/src/index.ts +++ b/packages/authgear-react-native/src/index.ts @@ -75,15 +75,17 @@ export { export * from "./ui_implementation"; /** + * ConfigureOptions is options for configuring the Authgear SDK container. + * * @public */ export interface ConfigureOptions { /** - * The OAuth client ID. + * The OAuth client ID. You may find this value in Authgear Portal (Your project \> Applications). */ clientID: string; /** - * The endpoint. + * The endpoint. You may find this value in Authgear Portal (Your project \> Applications). */ endpoint: string; @@ -107,8 +109,8 @@ export interface ConfigureOptions { */ preAuthenticatedURLEnabled?: boolean; - /* - * The UIImplementation. + /** + * The implementation of UIImplementation. */ uiImplementation?: UIImplementation; } @@ -177,13 +179,16 @@ export class ReactNativeContainer { wechatRedirectDeepLinkListener: (url: string) => void; /** + * Delegation for customizing the behavior of your application. + * You can implement your own delegation and the container will evaluate them if needed. + * * @public */ delegate?: ReactNativeContainerDelegate; /** - * - * Unique ID for this container. + * The name of the container. The name is used as the namespace of `TokenStorage`. + * One use case is to use multiple containers with different names to support signing in multiple accounts. * @defaultValue "default" * * @public @@ -236,6 +241,7 @@ export class ReactNativeContainer { } /** + * The current SessionState of this container. * * @public */ @@ -248,6 +254,7 @@ export class ReactNativeContainer { } /** + * The access token of this container. * * @public */ @@ -436,7 +443,12 @@ export class ReactNativeContainer { } /** - * Authenticate the end user via the web. + * authenticate() starts the authentication process in a {@link UIImplementation}. + * After authentication, the {@link UIImplementation} will be closed and the user is logged in. + * After this method resolves, biometric authentication is disabled. + * Call enableBiometric() again to enable biometric authentication for the new signed in user. + * + * You can refer to {@link AuthenticateOptions} for more customization. * * @public */ @@ -523,6 +535,11 @@ export class ReactNativeContainer { } /** + * changePassword() opens the settings page in a {@link UIImplementation} for the user to change their password. + * After changing the password, the {@link UIImplementation} will be closed. + * + * You can refer to {@link SettingsActionOptions} for more customization. + * * @public */ async changePassword(options: SettingsActionOptions): Promise { @@ -530,6 +547,11 @@ export class ReactNativeContainer { } /** + * deleteAccount() opens the settings page in a {@link UIImplementation} for the user to delete their account. + * After deletion, the {@link UIImplementation} will be closed and the user is logged out. + * + * You can refer to {@link SettingsActionOptions} for more customization. + * * @public */ async deleteAccount(options: SettingsActionOptions): Promise { @@ -538,10 +560,12 @@ export class ReactNativeContainer { } /** - * Reauthenticate the end user via biometric or the web. + * reauthenticate() starts the reauthentication process via biometric or in the {@link UIImplementation}. * * If biometricOptions is given, biometric is used when possible. * + * You can refer to {@link ReauthenticateOptions} and {@link BiometricOptions} for more customization. + * * @public */ async reauthenticate( @@ -658,18 +682,28 @@ export class ReactNativeContainer { await this.openURL(`${endpoint.origin}${path}`, options); } + /** + * Open Authgear pages. Currently only settings pages are available. + * + * You can refer to {@link SettingOptions} for more customization. + * + * @public + */ async open(page: Page, options?: SettingOptions): Promise { await this.openAuthgearURL(page, options); } /** - * Logout. + * logout() signs out the user without showing any UI. The refresh token will be cleared. + * The biometric authentication is kept, so the user can authenticateBiometric again. * * @remarks * If `force` parameter is set to `true`, all potential errors (e.g. network * error) would be ignored. * * @param options - Logout options + * + * @public */ async logout( options: { @@ -691,7 +725,10 @@ export class ReactNativeContainer { } /** - * Authenticate as an anonymous user. + * authenticateAnonymously() creates an anonymous user without showing any UI. + * You may first enable Anonymous Users in Authgear Portal (Your project \> Authentication \> Anonymous Users \> Enable anonymous users). + * + * @public */ async authenticateAnonymously(): Promise { const clientID = this.clientID; @@ -736,9 +773,14 @@ export class ReactNativeContainer { } /** - * Open promote anonymous user page + * promoteAnonymousUser() opens the anonymous user promotion page in the {@link UIImplementation} and the user has to authenticate. + * The flow is similar to authenticate(), the {@link UIImplementation} will be closed and the user is logged in after authentication. + * After this method resolves, biometric authentication is disabled. + * Call enableBiometric() again to enable biometric authentication for the new signed in user. + * + * You can refer to {@link PromoteOptions} for more customization. * - * @param options - promote options + * @public */ async promoteAnonymousUser( options: PromoteOptions @@ -819,7 +861,9 @@ export class ReactNativeContainer { } /** - * Fetch user info. + * fetchUserInfo() fetches the up-to-date user info. + * + * @public */ async fetchUserInfo(): Promise { await this.refreshAccessTokenIfNeeded(); @@ -839,6 +883,9 @@ export class ReactNativeContainer { } /** + * refreshAccessTokenIfNeeded() refreshes the access token if needed. + * After the task has completed, the updated access token will be stored in this.accessToken. + * * @public */ async refreshAccessTokenIfNeeded(): Promise { @@ -862,6 +909,8 @@ export class ReactNativeContainer { * * @param code - WeChat Authorization code. * @param state - WeChat Authorization state. + * + * @public */ async wechatAuthCallback(code: string, state: string): Promise { return this.baseContainer.apiClient._wechatAuthCallbackRequest( @@ -886,6 +935,8 @@ export class ReactNativeContainer { /** * Check whether biometric is supported on the current device. * If biometric is not supported, then a platform specific error is thrown. + * + * @public */ // eslint-disable-next-line class-methods-use-this async checkBiometricSupported(options: BiometricOptions): Promise { @@ -893,13 +944,21 @@ export class ReactNativeContainer { } /** - * Check whether biometric was enabled for the last logged in user. + * Check whether biometric was enabled for the signed in user. + * + * @public */ async isBiometricEnabled(): Promise { const keyID = await this.storage.getBiometricKeyID(this.name); return keyID != null; } + /** + * Disable biometric authentication for the signed in user. + * After disabling, the user may not be able to authenticate with biometric until it is enabled again. + * + * @public + */ async disableBiometric(): Promise { const keyID = await this.storage.getBiometricKeyID(this.name); if (keyID != null) { @@ -908,6 +967,15 @@ export class ReactNativeContainer { } } + /** + * Enable biometric authentication for the signed in user. + * Platform specific biometric authentication UI will be shown. + * You may first enable biometric authentication in Authgear Portal (Your Project \> Authentication \> Biometric \> Enable biometric authentication). + * + * You can refer to {@link BiometricOptions} for more customization. + * + * @public + */ async enableBiometric(options: BiometricOptions): Promise { const clientID = this.clientID; if (clientID == null) { @@ -945,6 +1013,15 @@ export class ReactNativeContainer { await this.storage.setBiometricKeyID(this.name, kid); } + /** + * Authenticate with biometric authentication. + * Platform specific biometric authentication UI will be shown. + * You may first enable biometric authentication in Authgear Portal (Your Project \> Authentication \> Biometric \> Enable biometric authentication). + * + * You can refer to {@link BiometricOptions} for more customization. + * + * @public + */ async authenticateBiometric( options: BiometricOptions ): Promise { @@ -1011,7 +1088,9 @@ export class ReactNativeContainer { /** * Share the current authenticated session to a web browser. * - * `preAuthenticatedURLEnabled` must be set to true to use this method. + * `ConfigureOptions.preAuthenticatedURLEnabled` must be set to true to use this method. + * + * You can refer to {@link PreAuthenticatedURLOptions} for more customization. * * @public */ diff --git a/packages/authgear-react-native/src/types.ts b/packages/authgear-react-native/src/types.ts index fa49626d..03ae2915 100644 --- a/packages/authgear-react-native/src/types.ts +++ b/packages/authgear-react-native/src/types.ts @@ -7,6 +7,10 @@ import { import { ReactNativeContainer } from "."; /** + * ReactNativeContainerDelegate defines a set of functions that the SDK container will use. + * + * You can implement these functions to customize the behavior of your application. + * * @public */ export interface ReactNativeContainerDelegate { @@ -196,7 +200,7 @@ export interface PromoteOptions { */ export interface AuthenticateResult { /** - * UserInfo. + * The updated user info after authentication. */ userInfo: UserInfo; } @@ -208,12 +212,15 @@ export interface AuthenticateResult { */ export interface ReauthenticateResult { /** - * UserInfo. + * The updated user info after reauthentication. */ userInfo: UserInfo; } /** + * Options that used by {@link ReactNativeContainer.open}. + * It allows you to configure the UI of the opened settings page. + * * @public */ export interface SettingOptions { @@ -238,6 +245,9 @@ export interface SettingOptions { } /** + * It is similar to {@link SettingOptions}, but it is used for configuring + * the UI of the opened settings page for specific action like `SettingsAction.ChangePassword`. + * * @public */ export interface SettingsActionOptions { @@ -335,7 +345,21 @@ export interface BiometricOptionsIOS { * @public */ localizedReason: string; + /** + * Set the contraint for the authenticator to be used for biometric authentication. + * + * See {@link BiometricAccessConstraintIOS} + * + * @public + */ constraint: BiometricAccessConstraintIOS; + /** + * Set the local authentication policy for biometric authentication. + * + * See {@link BiometricLAPolicy} + * + * @public + */ policy: BiometricLAPolicy; } @@ -393,6 +417,13 @@ export interface BiometricOptionsAndroid { * @public */ negativeButtonText: string; + /** + * Set the contraint for the authenticator to be used for biometric authentication. + * + * See {@link BiometricAccessConstraintAndroid} + * + * @public + */ constraint: BiometricAccessConstraintAndroid[]; /** * The user needs to set up biometric again when a new biometric is enrolled or all enrolled biometrics are removed. @@ -406,6 +437,7 @@ export interface BiometricOptionsAndroid { /** * BiometricOptions is options for biometric authentication. + * It allows platform-specific customization for iOS and Android. * * @public */ diff --git a/packages/authgear-react-native/src/ui_implementation.ts b/packages/authgear-react-native/src/ui_implementation.ts index 9f9c1328..b8fb88b7 100644 --- a/packages/authgear-react-native/src/ui_implementation.ts +++ b/packages/authgear-react-native/src/ui_implementation.ts @@ -1,19 +1,24 @@ import { openAuthorizeURL, openAuthorizeURLWithWebView } from "./nativemodule"; /** + * OpenAuthorizationURLOptions is options for {@link UIImplementation.openAuthorizationURL}. + * * @public */ export interface OpenAuthorizationURLOptions { - /* - * The URL to open. + /** + * The URL to be opened by the UIImplementation. */ url: string; - /* - * The URL to detect. + /** + * The URL to be detected by the UIImplementation. + * When this URL is detected, the UIImplementation MUST return this URL, and close itself. */ redirectURI: string; - /* - * A flag to some implementations that can share cookies with the device browser. + /** + * A flag to tell the UIImplementation that cookies should be shared with the device browser. + * This flag is only useful to UIImplementation that can share cookies with the device browser, + * such as those underlying implementations are based on ASWebAuthenticationSession, or CustomTabs. */ shareCookiesWithDeviceBrowser: boolean; } @@ -36,7 +41,10 @@ export interface UIImplementation { } /** - * DeviceBrowserUIImplementation is ASWebAuthenticationSession on iOS, and Custom Tabs on Android. + * DeviceBrowserUIImplementation is the default {@link UIImplementation}. + * + * For iOS, it is using ASWebAuthenticationSession (see https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession). + * For Android, it is using Custom Tabs (see https://developer.chrome.com/docs/android/custom-tabs). * * @public */ @@ -56,28 +64,52 @@ export class DeviceBrowserUIImplementation implements UIImplementation { } /** - * Color is an integer according to this encoding https://developer.android.com/reference/android/graphics/Color#encoding + * Color is an integer according to this encoding https://developer.android.com/reference/android/graphics/Color#encoding. + * Yes, it is still from Android such that the color encoding method in iOS is the same that used in Android. * * @public */ export interface WebKitWebViewUIImplementationOptionsIOS { + /** + * The color is in hexadecimal format representing the argb, for example, blue is 0xff0000ff. + */ navigationBarBackgroundColor?: number; + /** + * The color is in hexadecimal format representing the argb, for example, blue is 0xff0000ff. + */ navigationBarButtonTintColor?: number; + /** + * Styles for the modal. + * See https://developer.apple.com/documentation/uikit/uimodalpresentationstyle. + */ modalPresentationStyle?: "automatic" | "fullScreen" | "pageSheet"; + /** + * Indicates whether you can inspect the view with Safari Web Inspector. + * See https://developer.apple.com/documentation/webkit/wkwebview/4111163-isinspectable. + */ isInspectable?: boolean; } /** - * Color is an integer according to this encoding https://developer.android.com/reference/android/graphics/Color#encoding + * Color is an integer according to this encoding https://developer.android.com/reference/android/graphics/Color#encoding. * * @public */ export interface WebKitWebViewUIImplementationOptionsAndroid { + /** + * The color is in hexadecimal format representing the argb, for example, blue is 0xff0000ff. + */ actionBarBackgroundColor?: number; + /** + * The color is in hexadecimal format representing the argb, for example, blue is 0xff0000ff. + */ actionBarButtonTintColor?: number; } /** + * WebKitWebViewUIImplementationOptions specifies options for configuring the user interface of a WebKit WebView. + * It allows platform-specific customization for iOS and Android. + * * @public */ export interface WebKitWebViewUIImplementationOptions { @@ -86,7 +118,10 @@ export interface WebKitWebViewUIImplementationOptions { } /** - * WebKitWebViewUIImplementation is WKWebView on iOS, android.webkit.WebView on Android. + * WebKitWebViewUIImplementation provides more customization options other than {@link DeviceBrowserUIImplementation}. + * + * For iOS, it is using WKWebView (see https://developer.apple.com/documentation/webkit/wkwebview). + * For Android, it is using android.webkit.WebView (see https://developer.android.com/reference/android/webkit/WebView). * * @public */