From 26f35e792959679a7b29c2fa2b384e2df4debbc8 Mon Sep 17 00:00:00 2001 From: Subhankar Maiti Date: Wed, 7 May 2025 13:09:42 +0530 Subject: [PATCH] feat: add Consent Screen for user consent management --- src/App.tsx | 3 + src/screens/consent/index.tsx | 144 ++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 src/screens/consent/index.tsx diff --git a/src/App.tsx b/src/App.tsx index 1b012c8..0f8cec9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -51,6 +51,7 @@ const MfaRecoveryCodeEnrollmentScreen = React.lazy(() => import("./screens/mfa-r const ResetPasswordMfaPhoneChallengeScreen = React.lazy(() => import("./screens/reset-password-mfa-phone-challenge")); const PasskeyEnrollmentScreen = React.lazy(() => import("./screens/passkey-enrollment")); const MfaRecoveryCodeChallengeNewCodeScreen = React.lazy(() => import("./screens/mfa-recovery-code-challenge-new-code")); +const ConsentScreen = React.lazy(() => import("./screens/consent")); const App: React.FC = () => { @@ -162,6 +163,8 @@ const App: React.FC = () => { return ; case "mfa-recovery-code-challenge-new-code": return ; + case "consent": + return ; default: return <>No screen rendered; } diff --git a/src/screens/consent/index.tsx b/src/screens/consent/index.tsx new file mode 100644 index 0000000..a66c163 --- /dev/null +++ b/src/screens/consent/index.tsx @@ -0,0 +1,144 @@ +import React from 'react'; +import Consent from '@auth0/auth0-acul-js/consent'; +import type { Scope } from '@auth0/auth0-acul-js/consent'; + +const ConsentScreen: React.FC = () => { + // Instantiate the Consent screen manager + const consentManager = new Consent(); + // Destructure properties for easier access + const { screen, transaction, resourceServers, user, client, organization } = consentManager; + // Safely access scopes from screen.data + const scopes = screen.data?.scopes ?? []; + + // Handler for the Accept button + const handleAccept = async () => { + try { + await consentManager.accept(); + // On success, Auth0 handles redirection automatically. + } catch (error) { + console.error('Failed to accept consent:', error); + // TODO: Display an user-friendly error message + } + }; + + // Handler for the Deny button + const handleDeny = async () => { + try { + await consentManager.deny(); + // On success, Auth0 handles redirection automatically (usually back to app with error). + } catch (error) { + console.error('Failed to deny consent:', error); + // TODO: Display an user-friendly error message + } + }; + + // Get the primary resource server (API) information, if available + const primaryResourceServer = resourceServers && resourceServers.length > 0 ? resourceServers[0] : null; + + return ( +
+
+ {/* Client Information Header */} +
+ {client?.logoUrl && ( + {`${client.name + )} +
+

+ {client?.name || 'This Application'} wants access to your account +

+ {client?.description && ( +

{client.description}

+ )} +
+
+ + {/* User Information */} +
+ User avatar + + Logged in as {user?.email || user?.username || 'user'} + +
+ + {/* Organization Information (Optional) */} + {organization?.id && ( +
+ + Using organization: {organization.displayName || organization.name} + +
+ )} + + {/* Resource Server Information */} + {primaryResourceServer && ( +
+

To access the following API:

+

{primaryResourceServer.name}

+

Identifier: {primaryResourceServer.audience}

+
+ )} + + {/* Requested Permissions (Scopes) List */} +
+

This will allow the application to:

+ {scopes.length > 0 ? ( +
    + {scopes.map((scope: Scope) => ( +
  • + + + +
    + {scope.description || scope.name} + ({scope.name}) +
    +
  • + ))} +
+ ) : ( +

Review the requested permissions.

// Placeholder if scopes are empty + )} +
+ + {/* Transaction Error Display */} + {transaction?.errors && transaction.errors.length > 0 && ( +
+ Error: + {transaction.errors.map((err, index) => ( + {err.message} + ))} +
+ )} + + {/* Action Buttons */} +
+ + +
+
+
+ ); +}; + +export default ConsentScreen; \ No newline at end of file