Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Bundle errors from android and iOS into platform agnostic error codes
  • Loading branch information
kailash-b committed Feb 18, 2025
commit cdfe8a241f215b11abdf41fb764e4118fbdfbf83
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,31 @@ try {
}
```

**Platform agnostic errors:**

You can access the platform agnostic generic error codes as below :

```js
try {
const credentials = await auth0.credentialsManager.getCredentials();
} catch (error) {
console.log(e.generic_error_code);
}
```

| Generic Error Code | Corresponding Error Code in Android | Corresponding Error Code in iOS |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- |
| `INVALID_CREDENTIALS` | `INVALID_CREDENTIALS` | |
| `NO_CREDENTIALS` | `NO_CREDENTIALS` | `noCredentials` |
| `NO_REFRESH_TOKEN` | `NO_REFRESH_TOKEN` | `noRefreshToken` |
| `RENEW_FAILED` | `RENEW_FAILED` | `renewFailed` |
| `STORE_FAILED` | `STORE_FAILED` | `storeFailed` |
| `REVOKE_FAILED` | `REVOKE_FAILED` | `revokeFailed` |
| `LARGE_MIN_TTL` | `LARGE_MIN_TTL` | `largeMinTTL` |
| `INCOMPATIBLE_DEVICE` | `INCOMPATIBLE_DEVICE` | |
| `CRYPTO_EXCEPTION` | `CRYPTO_EXCEPTION` | |
| `BIOMETRICS_FAILED` | OneOf <br>`BIOMETRIC_NO_ACTIVITY`,`BIOMETRIC_ERROR_STATUS_UNKNOWN`,`BIOMETRIC_ERROR_UNSUPPORTED`,<br>`BIOMETRIC_ERROR_HW_UNAVAILABLE`,`BIOMETRIC_ERROR_NONE_ENROLLED`,`BIOMETRIC_ERROR_NO_HARDWARE`,<br>`BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED`,`BIOMETRIC_AUTHENTICATION_CHECK_FAILED`,<br>`BIOMETRIC_ERROR_DEVICE_CREDENTIAL_NOT_AVAILABLE` | `biometricsFailed` |

## Feedback

### Contributing
Expand Down
47 changes: 45 additions & 2 deletions android/src/main/java/com/auth0/react/A0Auth0Module.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,43 @@

public class A0Auth0Module extends ReactContextBaseJavaModule implements ActivityEventListener {

public static final Map<CredentialsManagerException, String> ERROR_CODE_MAP = new HashMap<>() {{
put(CredentialsManagerException.Companion.getINVALID_CREDENTIALS(), "INVALID_CREDENTIALS");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is Enum.name field provided by Enums in Kotlin, can we use that to simplify the code? I haven't fired up my IDE to test this but looks like there is an opportunity to clean this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Enums are internal in the underlying android SDK right now and we cannot access them.
We are leveraging the Public fields exposed via the Companion class.
However, we have a plan to migrate this .java code to .kt. Once that is done, we can leverage kotlin semantics and clean this snippet (tracked as part of SDK-5699)

put(CredentialsManagerException.Companion.getNO_CREDENTIALS(), "NO_CREDENTIALS");
put(CredentialsManagerException.Companion.getNO_REFRESH_TOKEN(), "NO_REFRESH_TOKEN");
put(CredentialsManagerException.Companion.getRENEW_FAILED(), "RENEW_FAILED");
put(CredentialsManagerException.Companion.getSTORE_FAILED(), "STORE_FAILED");
put(CredentialsManagerException.Companion.getREVOKE_FAILED(), "REVOKE_FAILED");
put(CredentialsManagerException.Companion.getLARGE_MIN_TTL(), "LARGE_MIN_TTL");
put(CredentialsManagerException.Companion.getINCOMPATIBLE_DEVICE(), "INCOMPATIBLE_DEVICE");
put(CredentialsManagerException.Companion.getCRYPTO_EXCEPTION(), "CRYPTO_EXCEPTION");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_NO_ACTIVITY(), "BIOMETRIC_NO_ACTIVITY");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_STATUS_UNKNOWN(), "BIOMETRIC_ERROR_STATUS_UNKNOWN");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_UNSUPPORTED(), "BIOMETRIC_ERROR_UNSUPPORTED");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_HW_UNAVAILABLE(), "BIOMETRIC_ERROR_HW_UNAVAILABLE");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_NONE_ENROLLED(), "BIOMETRIC_ERROR_NONE_ENROLLED");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_NO_HARDWARE(), "BIOMETRIC_ERROR_NO_HARDWARE");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED(), "BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED");
put(CredentialsManagerException.Companion.getBIOMETRIC_AUTHENTICATION_CHECK_FAILED(), "BIOMETRIC_AUTHENTICATION_CHECK_FAILED");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_DEVICE_CREDENTIAL_NOT_AVAILABLE(), "BIOMETRIC_ERROR_DEVICE_CREDENTIAL_NOT_AVAILABLE");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_STRONG_AND_DEVICE_CREDENTIAL_NOT_AVAILABLE(), "BIOMETRIC_ERROR_STRONG_AND_DEVICE_CREDENTIAL_NOT_AVAILABLE");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL(), "BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_NEGATIVE_BUTTON(), "BIOMETRIC_ERROR_NEGATIVE_BUTTON");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_HW_NOT_PRESENT(), "BIOMETRIC_ERROR_HW_NOT_PRESENT");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_NO_BIOMETRICS(), "BIOMETRIC_ERROR_NO_BIOMETRICS");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_USER_CANCELED(), "BIOMETRIC_ERROR_USER_CANCELED");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_LOCKOUT_PERMANENT(), "BIOMETRIC_ERROR_LOCKOUT_PERMANENT");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_VENDOR(), "BIOMETRIC_ERROR_VENDOR");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_LOCKOUT(), "BIOMETRIC_ERROR_LOCKOUT");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_CANCELED(), "BIOMETRIC_ERROR_CANCELED");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_NO_SPACE(), "BIOMETRIC_ERROR_NO_SPACE");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_TIMEOUT(), "BIOMETRIC_ERROR_TIMEOUT");
put(CredentialsManagerException.Companion.getBIOMETRIC_ERROR_UNABLE_TO_PROCESS(), "BIOMETRIC_ERROR_UNABLE_TO_PROCESS");
put(CredentialsManagerException.Companion.getBIOMETRICS_INVALID_USER(), "BIOMETRICS_INVALID_USER");
put(CredentialsManagerException.Companion.getBIOMETRIC_AUTHENTICATION_FAILED(), "BIOMETRIC_AUTHENTICATION_FAILED");
put(CredentialsManagerException.Companion.getAPI_ERROR(), "API_ERROR");
put(CredentialsManagerException.Companion.getNO_NETWORK(), "NO_NETWORK");
}};
private static final String CREDENTIAL_MANAGER_ERROR_CODE = "a0.invalid_state.credential_manager_exception";
private static final String INVALID_DOMAIN_URL_ERROR_CODE = "a0.invalid_domain_url";
private static final String BIOMETRICS_AUTHENTICATION_ERROR_CODE = "a0.invalid_options_biometrics_authentication";
Expand Down Expand Up @@ -122,18 +159,24 @@ public void onSuccess(Credentials credentials) {

@Override
public void onFailure(@NonNull CredentialsManagerException e) {
promise.reject(CREDENTIAL_MANAGER_ERROR_CODE, e.getMessage(), e);
String errorCode = deduceErrorCode(e);
promise.reject(errorCode, e.getMessage(), e);
}
}));
}

private String deduceErrorCode(@NonNull CredentialsManagerException e) {
return ERROR_CODE_MAP.getOrDefault(e, CREDENTIAL_MANAGER_ERROR_CODE);
}

@ReactMethod
public void saveCredentials(ReadableMap credentials, Promise promise) {
try {
this.secureCredentialsManager.saveCredentials(CredentialsParser.fromMap(credentials));
promise.resolve(true);
} catch (CredentialsManagerException e) {
promise.reject(CREDENTIAL_MANAGER_ERROR_CODE, e.getMessage(), e);
String errorCode = deduceErrorCode(e);
promise.reject(errorCode, e.getMessage(), e);
}
}

Expand Down
50 changes: 50 additions & 0 deletions src/credentials-manager/credentialsManagerError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,86 @@ export default class CredentialsManagerError<
public json;
public status;
public invalid_parameter;
public generic_error_code;

constructor(response: Auth0Response<CredentialsManagerErrorDetails>) {
const { status, json, text } = response;
const {
error,
error_description: description,
invalid_parameter,
code,
}: {
error?: string;
error_description?: string;
invalid_parameter?: string;
code?: string;
} = json ?? {
error: undefined,
error_description: undefined,
invalid_parameter: undefined,
code: undefined,
};
super(
error || 'a0.response.invalid',
description || text || handleInvalidToken(response) || 'unknown error'
);
this.json = json;
this.status = status;
this.generic_error_code = this.convertToCommonErrorCode(code || '');

if (invalid_parameter) {
this.invalid_parameter = invalid_parameter;
}
}

convertToCommonErrorCode(code: string): string {
let errorMapping: Record<string, string> = {
INVALID_CREDENTIALS: 'INVALID_CREDENTIALS',
NO_CREDENTIALS: 'NO_CREDENTIALS',
NO_REFRESH_TOKEN: 'NO_REFRESH_TOKEN',
RENEW_FAILED: 'RENEW_FAILED',
STORE_FAILED: 'STORE_FAILED',
REVOKE_FAILED: 'REVOKE_FAILED',
LARGE_MIN_TTL: 'LARGE_MIN_TTL',
INCOMPATIBLE_DEVICE: 'INCOMPATIBLE_DEVICE',
CRYPTO_EXCEPTION: 'CRYPTO_EXCEPTION',
BIOMETRIC_NO_ACTIVITY: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_STATUS_UNKNOWN: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_UNSUPPORTED: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_HW_UNAVAILABLE: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_NONE_ENROLLED: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_NO_HARDWARE: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: 'BIOMETRICS_FAILED',
BIOMETRIC_AUTHENTICATION_CHECK_FAILED: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_DEVICE_CREDENTIAL_NOT_AVAILABLE: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_STRONG_AND_DEVICE_CREDENTIAL_NOT_AVAILABLE:
'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_NEGATIVE_BUTTON: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_HW_NOT_PRESENT: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_NO_BIOMETRICS: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_USER_CANCELED: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_LOCKOUT_PERMANENT: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_VENDOR: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_LOCKOUT: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_CANCELED: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_NO_SPACE: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_TIMEOUT: 'BIOMETRICS_FAILED',
BIOMETRIC_ERROR_UNABLE_TO_PROCESS: 'BIOMETRICS_FAILED',
BIOMETRICS_INVALID_USER: 'BIOMETRICS_FAILED',
BIOMETRIC_AUTHENTICATION_FAILED: 'BIOMETRICS_FAILED',
BIOMETRICS_FAILED: 'BIOMETRICS_FAILED',
NO_NETWORK: 'NO_NETWORK',
API_ERROR: 'API_ERROR',
};
return errorMapping[code] || 'UNKNOWN_ERROR';
}
}

export interface CredentialsManagerErrorDetails {
error?: string;
error_description?: string;
invalid_parameter?: string;
code?: string;
}
19 changes: 13 additions & 6 deletions src/credentials-manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class CredentialsManager {
const json = {
error: 'a0.credential_manager.invalid',
error_description: e.message,
code: e.code,
};
throw new CredentialsManagerError({ json, status: 0 });
}
Expand Down Expand Up @@ -79,12 +80,18 @@ class CredentialsManager {
this.domain,
this.localAuthenticationOptions
);
return this.Auth0Module.getCredentials(
scope,
minTtl,
parameters,
forceRefresh
);
return new Promise<Credentials>((resolve, reject) => {
this.Auth0Module.getCredentials(scope, minTtl, parameters, forceRefresh)
.then(resolve)
.catch((e) => {
const json = {
error: 'a0.credential_manager.invalid',
error_description: e.message,
code: e.code,
};
reject(new CredentialsManagerError({ json, status: 0 }));
});
});
} catch (e) {
const json = {
error: 'a0.credential_manager.invalid',
Expand Down
6 changes: 4 additions & 2 deletions src/utils/baseError.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
class BaseError extends Error {
constructor(name: string, message: string) {
generic_error_code: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use field name like

  • identifier
  • reason
  • type

Or something that will be generic and can fit in a single word.

The purpose of the field will be extensively documented so it would be a one time learning that it is a platform agnostic error code.

constructor(name: string, message: string, generic_error_code?: string) {
super();
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
this.name = name;
this.message = message;
this.generic_error_code = generic_error_code || '';
}
}

export default BaseError;
export default BaseError;