### Singpass MyInfo Configuration Example (PHP) Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md This is an example of the published configuration file for the Singpass MyInfo Laravel package. It includes settings for discovery endpoints, client credentials, key file paths, scopes, and controller mappings for authorization and callback handling. ```php return [ // default to Singpass staging 'openid_discovery_endpoint' => env('SINGPASS_OPENID_DISCOVERY_ENDPOINT', 'https://stg-id.singpass.gov.sg/.well-known/openid-configuration'), 'client_id' => env('SINGPASS_CLIENT_ID'), // this setting is here because socialite requires it to be defined. SingpassProvider will always overwrite it to route('singpass.callback') 'redirect' => env('SINGPASS_REDIRECT_URI'), // the private key file that your application will be used for signing 'signing_private_key_passphrase' => env('SINGPASS_SIGNING_PRIVATE_KEY_PASSPHRASE', ''), 'signing_private_key_file' => env('SINGPASS_SIGNING_PRIVATE_KEY_FILE'), // the private key file that your application will be used for decryption 'decryption_private_key_passphrase' => env('SINGPASS_DECRYPTION_PRIVATE_KEY_PASSPHRASE', ''), 'decryption_private_key_file' => env('SINGPASS_DECRYPTION_PRIVATE_KEY_FILE'), // used by socialite. leave it empty since Singpass uses client assertion 'client_secret' => '', // default to Singpass login if SINGPASS_SCOPES is blank. for MyInfo, define additional scopes that are space separated // e.g. "openid uinfin name sex race dob birthcountry passportnumber" 'scopes' => env('SINGPASS_SCOPES', 'openid'), // this is the route that will be used to redirect to Singpass login page // you can customize this in .env file 'authorization_endpoint' => env('SINGPASS_AUTHORIZATION_ENDPOINT', 'sp/login'), // the controller that will handle the redirection to Singpass login page // to customize, you can replace it with your own controller in this config file 'authorization_endpoint_controller' => GetAuthorizationController::class, // this is the route that will be called when Singpass redirects back after authentication // you can customize this in .env file 'callback_endpoint' => env('SINGPASS_CALLBACK_ENDPOINT', 'sp/callback'), // the controller that will handle the callback from Singpass after login // to customize, you can replace it with your own controller in this config file 'callback_endpoint_controller' => GetCallbackController::class, // this is the url that Singpass will call to retrieve your public jwks for signing and encryption // you can customize this in .env file 'jwks_endpoint' => env('SINGPASS_JWKS_ENDPOINT', 'sp/jwks'), // the controller that Singpass portal will use to retrieve your application jwks // typically you won't want to change it unless you want to implement key rotation logic 'jwks_endpoint_controller' => GetJwksController::class, ]; ``` -------------------------------- ### Install Singpass MyInfo Package via Composer Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Installs the shokanshi/singpass-myinfo package using Composer. This is the primary method for adding the package to your Laravel project. ```bash composer require shokanshi/singpass-myinfo ``` -------------------------------- ### Implementing Robust Exception Handling Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt This example demonstrates how to handle specific Singpass-related exceptions during the callback process, ensuring secure and informative error responses for authentication, token exchange, and decryption failures. ```php public function callback(Request $request) { try { $user = singpass()->user(); return response()->json(['success' => true, 'user' => $user->getRaw()]); } catch (InvalidStateException $e) { return response()->json(['error' => 'invalid_state'], 400); } catch (SingpassTokenException $e) { return response()->json(['error' => 'token_error'], $e->getCode()); } catch (JweInvalidException $e) { return response()->json(['error' => 'jwe_error'], $e->getCode()); } catch (JwtPayloadException $e) { return response()->json(['error' => 'jwt_validation_error'], 400); } catch (SingpassPrivateKeyMissingException $e) { return response()->json(['error' => 'configuration_error'], 500); } catch (\Exception $e) { report($e); return response()->json(['error' => 'unexpected_error'], 500); } } ``` -------------------------------- ### Configure Singpass Environment Variables Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md This snippet shows the necessary environment variables to be added to your `.env` file for Singpass integration. It includes client ID, private key file paths, passphrases, discovery endpoints, and scopes. Adjust these values according to your Singpass application setup. ```ini # Singpass variables SINGPASS_CLIENT_ID= # Base folder is ./storage/app SINGPASS_SIGNING_PRIVATE_KEY_FILE=secure/your-singpass-signing-private.pem SINGPASS_SIGNING_PRIVATE_KEY_PASSPHRASE= # Base folder is ./storage/app SINGPASS_DECRYPTION_PRIVATE_KEY_FILE=secure/your-singpass-decryption-private.pem SINGPASS_DECRYPTION_PRIVATE_KEY_PASSPHRASE= SINGPASS_OPENID_DISCOVERY_ENDPOINT=https://stg-id.singpass.gov.sg/.well-known/openid-configuration # for Singpass login, set openid as the only scope. Additional scopes (space separated within double quotes) will switch to MyInfo flow SINGPASS_SCOPES="openid" # Default routes SINGPASS_AUTHORIZATION_ENDPOINT=sp/login SINGPASS_CALLBACK_ENDPOINT=sp/callback SINGPASS_JWKS_ENDPOINT=sp/jwks ``` -------------------------------- ### GET /sp/callback Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Handles the callback from Singpass after user authorization, exchanging the authorization code for an ID token and user information. ```APIDOC ## GET /sp/callback ### Description Processes the authorization code returned by Singpass. It validates the state, performs the token exchange, and retrieves the authenticated user's profile. ### Method GET ### Endpoint /sp/callback ### Parameters #### Query Parameters - **code** (string) - Required - The authorization code provided by Singpass. - **state** (string) - Required - The CSRF protection state string. ### Response #### Success Response (200) - **user** (object) - The authenticated user object containing Singpass identity information. #### Response Example { "id": "S1234567A", "name": "John Doe", "email": "john@example.com" } ``` -------------------------------- ### GET /sp/login Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Initiates the Singpass authentication flow by redirecting the user to the Singpass authorization server. This method handles PKCE, DPoP, and Pushed Authorization Requests (PAR) automatically. ```APIDOC ## GET /sp/login ### Description Initiates the OAuth 2.0 authorization code flow with Singpass. It generates the necessary security artifacts (PKCE, DPoP, Client Assertion) and performs a Pushed Authorization Request (PAR) before redirecting the user. ### Method GET ### Endpoint /sp/login ### Parameters #### Query Parameters - **None** (N/A) - The configuration is handled via the package settings and session state. ### Request Example ```php return singpass()->redirect(); ``` ### Response #### Success Response (302) - **Location** (string) - Redirects the user to the Singpass authorization server URL. #### Response Example HTTP/1.1 302 Found Location: https://stg-id.singpass.gov.sg/authorize?client_id=...&request_uri=urn:ietf:params:oauth:request_uri:... ``` -------------------------------- ### Get Singpass Socialite Provider Instance Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Demonstrates how to obtain the SingpassProvider Socialite instance using the `singpass()` helper function or the Socialite facade. This instance is used to initiate authentication flows and retrieve user data. ```php redirect(); // Get the authorization URL without redirecting $authUrl = singpass()->redirect()->getTargetUrl(); // Returns: https://stg-id.singpass.gov.sg/authorize?client_id=xxx&request_uri=urn:ietf:params:oauth:request_uri:xxx ``` -------------------------------- ### Override OpenID Discovery URL with setOpenIdDiscoveryUrl() Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt The `setOpenIdDiscoveryUrl()` method enables runtime configuration of the Singpass OpenID Connect discovery endpoint. This is beneficial for managing different environments (staging, production) or for multitenant setups where tenants might use distinct endpoints. ```php environment('production') ? 'https://id.singpass.gov.sg/.well-known/openid-configuration' : 'https://stg-id.singpass.gov.sg/.well-known/openid-configuration'; return singpass() ->setOpenIdDiscoveryUrl($discoveryUrl) ->setAuthenticationContextType('account_login') ->redirect(); } } ``` -------------------------------- ### Method: when Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Conditional execution utility for method chaining. ```APIDOC ## when($value, ?callable $callback, ?callable $default) ### Description Allows conditional execution of a closure within a method chain. Useful for applying modifications based on dynamic conditions. ### Parameters - **$value** (boolean) - Required - The condition to evaluate. - **$callback** (callable) - Optional - Function to execute if $value is truthy. - **$default** (callable) - Optional - Function to execute if $value is falsy. ``` -------------------------------- ### Configure Environment-Specific Authentication Controller Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Demonstrates how to create and configure a custom controller to switch between local and production Singpass configurations. This involves generating the controller via Artisan, registering it in the config file, and implementing environment-aware logic within the controller. ```bash php artisan make:controller MySingpassAuthController ``` ```php 'authorization_endpoint_controller' => MySingpassAuthController::class, ``` ```php class MySingpassAuthController extends Controller { public function __invoke(Request $request) { return singpass() ->when(app()->environment('local'), function($singpass) { $singpass ->setClientId('staging client id') ->setOpenIdDiscoveryUrl('https://stg-id.singpass.gov.sg/.well-known/openid-configuration') ->addSigningKey(Storage::disk('local')->get('stage_signing_key_1.pem')) ->addDecryptionKey(Storage::disk('local')->get('stage_decryption_key_1.pem')); }) ->when(app()->environment('production'), function($singpass) { $singpass ->setClientId('production client id') ->setOpenIdDiscoveryUrl('https://id.singpass.gov.sg/.well-known/openid-configuration') ->addSigningKey(Storage::disk('local')->get('prod_signing_key_1.pem')) ->addDecryptionKey(Storage::disk('local')->get('prod_decryption_key_1.pem')); }) ->redirect(); } } ``` -------------------------------- ### Apply Conditional Configuration with when() Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt The when() method allows for conditional configuration within a fluent API chain. It is primarily used to dynamically select client IDs, discovery URLs, and cryptographic keys based on the current application environment. ```php when(app()->environment('local'), function ($singpass) { $singpass ->setClientId(config('singpass.staging.client_id')) ->setOpenIdDiscoveryUrl('https://stg-id.singpass.gov.sg/.well-known/openid-configuration') ->addSigningKey(Storage::disk('local')->get('keys/staging-signing.pem')) ->addDecryptionKey(Storage::disk('local')->get('keys/staging-decryption.pem')); }) ->when(app()->environment('production'), function ($singpass) { $singpass ->setClientId(config('singpass.production.client_id')) ->setOpenIdDiscoveryUrl('https://id.singpass.gov.sg/.well-known/openid-configuration') ->addSigningKey(Storage::disk('local')->get('keys/production-signing.pem')) ->addDecryptionKey(Storage::disk('local')->get('keys/production-decryption.pem')); }) ->setAuthenticationContextType('account_login') ->redirect(); } } ``` -------------------------------- ### Generate Private Keys for Singpass Signing and Decryption Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md This section provides bash commands to generate private keys for both signing and decryption using OpenSSL. These keys are essential for secure communication with Singpass. Ensure you store these keys securely and do not expose them publicly. ```bash openssl ecparam -name prime256v1 -genkey -noout -out ./storage/app/secure/your-singpass-signing-private.pem openssl ecparam -name prime256v1 -genkey -noout -out ./storage/app/secure/your-singpass-decryption-private.pem ``` -------------------------------- ### Add Signing Keys with addSigningKey() for Assertions and DPoP Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt The `addSigningKey()` method allows adding signing private keys directly from PEM content, supporting client assertion and DPoP proof generation. It bypasses file-based configuration and allows multiple keys for rotation, with the first added key being used for signing. ```php user()->tenant; // Load tenant-specific signing key from database or secure storage $signingKeyPem = decrypt($tenant->singpass_signing_key); $passphrase = decrypt($tenant->singpass_key_passphrase); return singpass() ->setClientId($tenant->singpass_client_id) ->addSigningKey($signingKeyPem, $passphrase) ->setAuthenticationContextType('account_login') ->redirect(); } public function loginWithKeyRotation(Request $request): RedirectResponse { // Support multiple signing keys for rotation // Only keys within valid date range are added $tenant = $request->user()->tenant; $now = now(); $provider = singpass()->setClientId($tenant->singpass_client_id); foreach ($tenant->signingKeys()->where('type', 'signing')->get() as $key) { if ($now->between($key->valid_from, $key->valid_to)) { $provider->addSigningKey( decrypt($key->key_content), decrypt($key->passphrase) ); } } return $provider ->setAuthenticationContextType('account_login') ->redirect(); } } ``` -------------------------------- ### Publish Singpass MyInfo Configuration File Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Publishes the configuration file for the Singpass MyInfo package, allowing you to customize settings like endpoints, client IDs, and key file paths. This command uses the 'singpass-myinfo-config' tag. ```bash php artisan vendor:publish --tag="singpass-myinfo-config" ``` -------------------------------- ### Configure Client and Endpoint Settings Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Methods to dynamically override environment configurations for the Singpass client ID, OpenID discovery URL, and redirect URI. ```php $auth->setClientId("MY_CLIENT_ID") ->setOpenIdDiscoveryUrl("https://stg-id.singpass.gov.sg/.well-known/openid-configuration") ->setRedirectUrl("https://myapp.com/callback"); ``` -------------------------------- ### Method: generateJwksForSingpassPortal Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Generates public keys for Singpass consumption. ```APIDOC ## generateJwksForSingpassPortal() ### Description Returns an array of public keys that will be JSON encoded and consumed by the Singpass portal. ### Response #### Success Response (200) - **array** (array) - A collection of public keys formatted as a JWKS. ``` -------------------------------- ### Conditional Method Chaining with when() Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Provides a fluent interface to execute closures conditionally within a method chain. This avoids breaking chains with traditional if statements. ```php public function when($value, ?callable $callback = null, ?callable $default = null): self; // Usage example: $instance->when($condition, function($instance) { return $instance->doSomething(); }); ``` -------------------------------- ### addSigningKey() Method Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Adds a signing private key from PEM content for client assertion and DPoP proof generation. Accepts raw PEM key content and supports multiple keys for rotation. ```APIDOC ## addSigningKey() Method ### Description Adds a signing private key from PEM content for client assertion and DPoP proof generation. Accepts raw PEM key content and supports multiple keys for rotation. ### Method Not applicable (this is a method call within a larger process). ### Endpoint Not applicable. ### Parameters #### Path Parameters None. #### Query Parameters None. #### Request Body None. ### Request Example ```php // Example usage within a controller method $signingKeyPem = decrypt($tenant->singpass_signing_key); $passphrase = decrypt($tenant->singpass_key_passphrase); singpass() ->setClientId($tenant->singpass_client_id) ->addSigningKey($signingKeyPem, $passphrase) ->setAuthenticationContextType('account_login') ->redirect(); // Example with key rotation $provider = singpass()->setClientId($tenant->singpass_client_id); foreach ($tenant->signingKeys()->where('type', 'signing')->get() as $key) { if (now()->between($key->valid_from, $key->valid_to)) { $provider->addSigningKey( decrypt($key->key_content), decrypt($key->passphrase) ); } } return $provider->setAuthenticationContextType('account_login')->redirect(); ``` ### Response #### Success Response (200) Not applicable (this method is part of a fluent interface for building a redirect response). #### Response Example None. ``` -------------------------------- ### Generate JWKS for Singpass MyInfo Endpoint (PHP) Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md This controller generates a JWKS for the Singpass MyInfo portal. It retrieves tenant-specific Singpass configurations and private keys, then uses them to generate the JWKS. Dependencies include the Tenant model, Carbon for date comparisons, and a custom 'singpass' facade. ```php class MySingpassJwksEndpointController extends Controller { public function __invoke(Request $request) { $tenant = Tenant::current(); singpass() ->setClientId($tenant->singpass_client_id) ->setOpenIdDiscoveryUrl($tenant->singpass_openid_discovery_endpoint) ->setScopes([$tenant->singpass_scopes]); foreach ($tenant->singpassPrivateKeys() as $key) { singpass() ->when(Carbon::now()->between($key->valid_from, $key->valid_to), function($singpass) use ($key) { $singpass ->when($key->type === 'signing', function($singpass) use ($key) { $singpass->addSigningKey($key->key_content, $key->passphrase); }) ->when($key->type === 'decryption', function($singpass) use ($key) { $singpass->addDecryptionKey($key->key_content, $key->passphrase); }); }); } return response()->json(json_encode(singpass()->generateJwksForSingpassPortal())); } } ``` -------------------------------- ### Method: addDecryptionKeyFromJsonObject Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Adds a new private key to the collection and updates the decryption key configuration. ```APIDOC ## addDecryptionKeyFromJsonObject(string $json) ### Description Adds a new private key to the collection and overwrites the value of `SINGPASS_DECRYPTION_PRIVATE_KEY_FILE` defined in the .env file. ### Parameters #### Request Body - **$json** (string) - Required - Json encoded string of the private JWK used for decryption. ### Request Example { "alg": "ECDH-ES+A256KW", "kty": "EC", "x": "_TSrfW3arG1Ebc8pCyT-r5lAFvCh_rJvC5HD5-y8yvs", "y": "Sr2vpuU6gzdUiXddGnRJIroXCfdameaR1mgU49H5h9A", "crv": "P-256", "d": "AEabUwi3VjOOfiyoOtSGrqpl8cfhcUhNtj-xh1l-UYE", "use": "enc", "kid": "my-enc-key" } ``` -------------------------------- ### Configure Environment Variables for Singpass Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Sets up environment variables in the .env file for Singpass integration, including client ID, private key paths, endpoint URLs, and scopes for staging or production environments. ```ini # .env file configuration # Your Singpass application client ID from Developer Portal SINGPASS_CLIENT_ID=your-client-id-here # Private key paths (relative to storage/app/) SINGPASS_SIGNING_PRIVATE_KEY_FILE=secure/singpass-signing-private.pem SINGPASS_SIGNING_PRIVATE_KEY_PASSPHRASE= SINGPASS_DECRYPTION_PRIVATE_KEY_FILE=secure/singpass-decryption-private.pem SINGPASS_DECRYPTION_PRIVATE_KEY_PASSPHRASE= # OpenID Discovery Endpoint # Staging: https://stg-id.singpass.gov.sg/.well-known/openid-configuration # Production: https://id.singpass.gov.sg/.well-known/openid-configuration SINGPASS_OPENID_DISCOVERY_ENDPOINT=https://stg-id.singpass.gov.sg/.well-known/openid-configuration # Scopes: "openid" for login only, add more for MyInfo data # Example for MyInfo: "openid uinfin name sex race dob birthcountry passportnumber" SINGPASS_SCOPES="openid" # Route endpoints (customizable) SINGPASS_AUTHORIZATION_ENDPOINT=sp/login SINGPASS_CALLBACK_ENDPOINT=sp/callback SINGPASS_JWKS_ENDPOINT=sp/jwks ``` -------------------------------- ### Manage Signing and Decryption Keys Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Methods to add private keys for signing requests or decrypting responses. Supports raw PEM content or JSON-encoded JWK objects. ```php $auth->addSigningKey($pemContent, "optional-passphrase"); $auth->addSigningKeyFromJsonObject('{"alg":"ES256","kty":"EC","x":"...","y":"...","crv":"P-256","d":"...","use":"sig","kid":"my-sig-key"}'); $auth->addDecryptionKey($decryptionPemContent); ``` -------------------------------- ### Access Socialite Provider Methods Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Core methods for interacting with the Singpass Socialite provider, including retrieving the provider instance, the authenticated user object, and handling redirects. ```php // Get provider instance $provider = singpass(); // Or via Socialite $provider = Socialite::driver('singpass'); // Get user $user = singpass()->user(); // Redirect to provider return singpass()->redirect(); // Get redirect URL $url = singpass()->redirect()->getTargetUrl(); ``` -------------------------------- ### Exception Handling in Callback Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Robust exception handling for the callback endpoint to manage various Singpass-specific errors gracefully. ```APIDOC ## Exception Handling for Callback ### Description Provides detailed error responses for various exceptions that may occur during the Singpass callback process, ensuring robust error management. ### Method POST (within the callback endpoint logic) ### Endpoint /sp/callback (handled internally) ### Parameters N/A (Exceptions are caught during the processing of the callback request) ### Response #### Error Responses (Examples) - **InvalidStateException** - **error**: "invalid_state" - **message**: "Session expired. Please try logging in again." - **Status Code**: 400 - **SingpassTokenException** - **error**: "token_error" - **message**: "[Specific token error message]" - **Status Code**: [HTTP status code from exception] - **JweInvalidException** - **error**: "jwe_error" - **message**: "[Specific JWE error message]" - **Status Code**: [HTTP status code from exception] - **JwtPayloadException** - **error**: "jwt_validation_error" - **message**: "[Specific JWT payload error message]" - **Status Code**: 400 - **SingpassPrivateKeyMissingException** - **error**: "configuration_error" - **message**: "Server configuration error. Please contact support." - **Status Code**: 500 - **Unexpected Exception** - **error**: "unexpected_error" - **message**: "An unexpected error occurred." - **Status Code**: 500 ``` -------------------------------- ### Custom Controller Configuration Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt You can replace the default controllers with your custom implementations by specifying their class names in the `config/singpass-myinfo.php` configuration file. ```APIDOC ## Configuration ### Description Customize the Singpass MyInfo integration by replacing default controllers with your own implementations. ### Method N/A (Configuration File) ### Endpoint N/A (Configuration File) ### Parameters #### Request Body (config/singpass-myinfo.php) - **authorization_endpoint_controller** (string) - Optional - The fully qualified class name of your custom controller for the authorization endpoint. - **callback_endpoint_controller** (string) - Optional - The fully qualified class name of your custom controller for the callback endpoint. - **jwks_endpoint_controller** (string) - Optional - The fully qualified class name of your custom controller for the JWKS endpoint. ### Request Example ```php // config/singpass-myinfo.php return [ // ... other config options 'authorization_endpoint_controller' => \App\Http\Controllers\MySingpassLoginController::class, 'callback_endpoint_controller' => \App\Http\Controllers\MySingpassCallbackController::class, 'jwks_endpoint_controller' => \App\Http\Controllers\MySingpassJwksController::class, ]; ``` ### Response N/A (Configuration changes are applied on application load.) ``` -------------------------------- ### Add Signing Key from JWK Object Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Configures the signing private key using a JSON-encoded JWK string. This is typically used when keys are retrieved from secure key management services or stored in JSON format. ```php $signingJwk = json_encode([ 'alg' => 'ES256', 'kty' => 'EC', 'x' => 'tqG7PiAPD0xTBKdxDd4t8xAjJleP3Szw1CZiBjogmoc', 'y' => '256TjvubWV-x-C8lptl7eSbMa7pQUXH9LY1AIHUGINk', 'crv' => 'P-256', 'd' => 'PgL1UKVpvg_GeKdxV-oUEPIDhGBP2YYZLGiZ5HXDZDI', 'use' => 'sig', 'kid' => 'my-sig-key' ]); return singpass() ->addSigningKeyFromJsonObject($signingJwk) ->setAuthenticationContextType('account_login') ->redirect(); ``` -------------------------------- ### Generate JWKS for Singpass Portal Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Generates an array of public keys formatted for Singpass portal consumption. This array is intended to be JSON encoded. ```php public function generateJwksForSingpassPortal(): array; ``` -------------------------------- ### Configure Authentication Context Message Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Sets an optional authentication context message displayed to users. This is restricted to Login apps and has a maximum length of 100 characters. ```php $auth->setAuthenticationContextMessage("Please verify your identity to proceed."); ``` -------------------------------- ### Access Singpass Routes by Name in Laravel Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md This PHP code demonstrates how to access the default Singpass routes using Laravel's `route()` helper function. This allows for easy referencing of the login, callback, and JWKS endpoints within your application. ```php route('singpass.login'); route('singpass.callback'); route('singpass.jwks'); ``` -------------------------------- ### Configuring Custom Singpass Controllers Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Developers can override the default package controllers by specifying custom class paths in the 'config/singpass-myinfo.php' configuration file. ```php return [ 'authorization_endpoint_controller' => \App\Http\Controllers\MySingpassLoginController::class, 'callback_endpoint_controller' => \App\Http\Controllers\MySingpassCallbackController::class, 'jwks_endpoint_controller' => \App\Http\Controllers\MySingpassJwksController::class, ]; ``` -------------------------------- ### PHP Multitenant Singpass Login with Key Rotation Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Handles Singpass login for a multitenant application, supporting automatic key rotation by loading all valid signing and decryption keys for the current tenant. It sets the client ID and OpenID discovery URL, then iterates through tenant private keys, adding valid ones to the Singpass provider before redirecting for authentication. ```php setClientId($tenant->singpass_client_id) ->setOpenIdDiscoveryUrl($tenant->singpass_openid_discovery_endpoint); // Add all valid keys for key rotation support $tenant->privateKeys() ->where('provider', 'singpass') ->get() ->each(function (TenantPrivateKey $key) use ($provider, $now) { $provider->when( $now->between($key->valid_from, $key->valid_to), function ($singpass) use ($key) { $singpass ->when($key->type === 'signing', function ($singpass) use ($key) { $singpass->addSigningKey( decrypt($key->key_content), decrypt($key->passphrase) ); }) ->when($key->type === 'decryption', function ($singpass) use ($key) { $singpass->addDecryptionKey( decrypt($key->key_content), decrypt($key->passphrase) ); }); } ); }); return $provider ->setAuthenticationContextType($request->input('context', 'account_login')) ->redirect(); } public function callback(Request $request) { $tenant = Tenant::current(); $now = Carbon::now(); $provider = singpass() ->setClientId($tenant->singpass_client_id) ->setOpenIdDiscoveryUrl($tenant->singpass_openid_discovery_endpoint); // Load valid keys for token decryption $tenant->privateKeys() ->where('provider', 'singpass') ->get() ->each(function (TenantPrivateKey $key) use ($provider, $now) { $provider->when( $now->between($key->valid_from, $key->valid_to), fn($s) => $key->type === 'signing' ? $s->addSigningKey(decrypt($key->key_content), decrypt($key->passphrase)) : $s->addDecryptionKey(decrypt($key->key_content), decrypt($key->passphrase)) ); }); $user = $provider->user(); // Process authenticated user return response()->json([ 'tenant' => $tenant->name, 'user_id' => $user->getId(), 'name' => $user->getName(), 'profile' => $user->getRaw() ]); } public function jwks(Request $request): JsonResponse { $tenant = Tenant::current(); $now = Carbon::now(); $provider = singpass() ->setClientId($tenant->singpass_client_id) ->setOpenIdDiscoveryUrl($tenant->singpass_openid_discovery_endpoint); // Only expose public keys that are currently valid $tenant->privateKeys() ->where('provider', 'singpass') ->get() ->each(function (TenantPrivateKey $key) use ($provider, $now) { $provider->when( $now->between($key->valid_from, $key->valid_to), fn($s) => $key->type === 'signing' ? $s->addSigningKey(decrypt($key->key_content), decrypt($key->passphrase)) : $s->addDecryptionKey(decrypt($key->key_content), decrypt($key->passphrase)) ); }); return response()->json($provider->generateJwksForSingpassPortal()); } } ``` -------------------------------- ### Add Decryption Key from JSON Object Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Registers a private JWK for decryption and updates the SINGPASS_DECRYPTION_PRIVATE_KEY_FILE environment variable. It accepts a JSON-encoded string representing the private key. ```php public function addDecryptionKeyFromJsonObject(string $json): self; // Example usage with JWK: $json = '{ "alg": "ECDH-ES+A256KW", "kty": "EC", "x": "_TSrfW3arG1Ebc8pCyT-r5lAFvCh_rJvC5HD5-y8yvs", "y": "Sr2vpuU6gzdUiXddGnRJIroXCfdameaR1mgU49H5h9A", "crv": "P-256", "d": "AEabUwi3VjOOfiyoOtSGrqpl8cfhcUhNtj-xh1l-UYE", "use": "enc", "kid": "my-enc-key" }'; $instance->addDecryptionKeyFromJsonObject($json); ``` -------------------------------- ### Customize Singpass Controller Classes Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md This PHP configuration snippet illustrates how to override the default controller classes used for Singpass endpoints. By modifying the `singpass-myinfo.php` config file, you can point to your custom controllers for authorization, callback, and JWKS handling. ```php 'authorization_endpoint_controller' => GetAuthorizationController::class, 'callback_endpoint_controller' => GetCallbackController::class, 'jwks_endpoint_controller' => GetJwksController::class, ``` -------------------------------- ### Set Authentication Context Type Source: https://github.com/shokanshi/singpass-myinfo/blob/main/README.md Configures the authentication context type for Singpass Login app flows, which is required for anti-fraud purposes. ```php singpass()->setAuthenticationContextType('your_context_value'); ``` -------------------------------- ### setOpenIdDiscoveryUrl() Method Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Overrides the OpenID discovery endpoint for switching between staging and production environments. Allows runtime configuration of the Singpass OpenID Connect discovery endpoint. ```APIDOC ## setOpenIdDiscoveryUrl() Method ### Description Overrides the OpenID discovery endpoint for switching between staging and production environments. Allows runtime configuration of the Singpass OpenID Connect discovery endpoint. ### Method Not applicable (this is a method call within a larger process). ### Endpoint Not applicable. ### Parameters #### Path Parameters None. #### Query Parameters None. #### Request Body None. ### Request Example ```php // Example usage within a controller method $discoveryUrl = app()->environment('production') ? 'https://id.singpass.gov.sg/.well-known/openid-configuration' : 'https://stg-id.singpass.gov.sg/.well-known/openid-configuration'; singpass() ->setOpenIdDiscoveryUrl($discoveryUrl) ->setAuthenticationContextType('account_login') ->redirect(); ``` ### Response #### Success Response (200) Not applicable (this method is part of a fluent interface for building a redirect response). #### Response Example None. ``` -------------------------------- ### Redirect User to Singpass Authentication Page Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Implements the `redirect()` method within a Laravel controller to initiate the Singpass authentication process. This method handles PKCE, client assertions, DPoP, and Pushed Authorization Requests before redirecting the user. ```php redirect(); } } ``` -------------------------------- ### Set Client ID Dynamically with setClientId() Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt The `setClientId()` method allows overriding the default client ID at runtime. This is particularly useful for multitenant applications where each tenant requires a unique Singpass client ID. It bypasses the configuration file setting. ```php user()->tenant; return singpass() ->setClientId($tenant->singpass_client_id) ->setAuthenticationContextType('account_login') ->redirect(); } } ``` -------------------------------- ### Add Decryption Key from JWK Object Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Configures the decryption private key using a JSON-encoded JWK string. The key must utilize the ECDH-ES+A256KW algorithm to ensure FAPI 2.0 compliance. ```php $decryptionJwk = json_encode([ 'alg' => 'ECDH-ES+A256KW', 'kty' => 'EC', 'x' => '_TSrfW3arG1Ebc8pCyT-r5lAFvCh_rJvC5HD5-y8yvs', 'y' => 'Sr2vpuU6gzdUiXddGnRJIroXCfdameaR1mgU49H5h9A', 'crv' => 'P-256', 'd' => 'AEabUwi3VjOOfiyoOtSGrqpl8cfhcUhNtj-xh1l-UYE', 'use' => 'enc', 'kid' => 'my-enc-key' ]); $user = singpass() ->addSigningKeyFromJsonObject($signingJwk) ->addDecryptionKeyFromJsonObject($decryptionJwk) ->user(); ``` -------------------------------- ### Set Custom Redirect URL with setRedirectUrl() Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Use `setRedirectUrl()` to specify a custom callback URL, deviating from the default `route('singpass.callback')`. This is useful for applications requiring multiple callback handlers or tenant-specific redirect destinations. ```php 'registration']); return singpass() ->setRedirectUrl($callbackUrl) ->setAuthenticationContextType('signup') ->redirect(); } } ``` -------------------------------- ### Generate Public JWKS for Singpass Portal Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt The generateJwksForSingpassPortal method creates a public JWKS structure from configured signing and decryption keys. This output is intended to be exposed via a public endpoint to allow Singpass to verify application signatures and encrypt tokens. ```php generateJwksForSingpassPortal(); return response()->json($jwks); } } ``` -------------------------------- ### Registering Singpass Routes in Laravel Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt The package automatically registers essential routes for login, callback, and JWKS endpoints. Developers can reference these routes by name within their application to generate URLs. ```php Route::get('sp/login', GetAuthorizationController::class)->name('singpass.login'); Route::get('sp/callback', GetCallbackController::class)->name('singpass.callback'); Route::get('sp/jwks', GetJwksController::class)->name('singpass.jwks'); $loginUrl = route('singpass.login'); $callbackUrl = route('singpass.callback'); $jwksUrl = route('singpass.jwks'); ``` -------------------------------- ### Generate EC P-256 Private Keys for Singpass Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Generates EC P-256 private keys required for signing and decryption as per the Singpass FAPI 2.0 specification. These keys are essential for secure communication with Singpass services. ```bash # Create secure storage directory mkdir -p ./storage/app/secure # Generate signing private key (ES256) openssl ecparam -name prime256v1 -genkey -noout -out ./storage/app/secure/singpass-signing-private.pem # Generate decryption private key (ECDH-ES+A256KW) openssl ecparam -name prime256v1 -genkey -noout -out ./storage/app/secure/singpass-decryption-private.pem ``` -------------------------------- ### Singpass Integration Endpoints Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt The package automatically registers three default routes for Singpass integration: a login/authorization endpoint, a callback endpoint to handle Singpass responses, and a JWKS endpoint to expose public keys. ```APIDOC ## GET /sp/login ### Description This endpoint initiates the Singpass login and authorization process by redirecting the user to Singpass. ### Method GET ### Endpoint /sp/login ### Parameters #### Query Parameters None #### Request Body None ### Response Redirects to Singpass authorization page. ## GET /sp/callback ### Description This endpoint handles the callback from Singpass after user authorization, processing the response and retrieving user information. ### Method GET ### Endpoint /sp/callback ### Parameters #### Query Parameters - **code** (string) - Required - The authorization code received from Singpass. - **state** (string) - Required - The state parameter used to maintain state between the request and callback. #### Request Body None ### Response #### Success Response (200) - **success** (boolean) - Indicates if the operation was successful. - **user** (object) - Contains user information. - **id** (string) - The user's unique identifier. - **name** (string) - The user's name. - **profile** (object) - Raw user profile data from Singpass. #### Error Response (400, 401, 403, 404, 500) - **error** (string) - The type of error that occurred. - **message** (string) - A detailed message explaining the error. ## GET /sp/jwks ### Description This endpoint exposes the public keys (JWKS) used for verifying Singpass JWTs. ### Method GET ### Endpoint /sp/jwks ### Parameters #### Query Parameters None #### Request Body None ### Response #### Success Response (200) - **keys** (array) - An array of public keys in JWK format. - **kty** (string) - Key Type (e.g., "RSA"). - **kid** (string) - Key ID. - **use** (string) - Public Key Use (e.g., "sig"). - **n** (string) - RSA modulus. - **e** (string) - RSA public exponent. ``` -------------------------------- ### Add Decryption Key from PEM Content Source: https://context7.com/shokanshi/singpass-myinfo/llms.txt Adds a decryption private key using raw PEM format. This method is essential for decrypting JWE-encrypted tokens and supports multiple keys for rotation. ```php $signingKeyPem = decrypt($tenant->singpass_signing_key); $decryptionKeyPem = decrypt($tenant->singpass_decryption_key); $user = singpass() ->setClientId($tenant->singpass_client_id) ->addSigningKey($signingKeyPem) ->addDecryptionKey($decryptionKeyPem) ->user(); ```