Ozone

Open Finance Malaysia Developer Portal

Section 7: Authorizing a Consent

After submitting your consent request via the PAR endpoint and receiving a request_uri, the next step is redirecting your user to the OFP's authorization endpoint. The user will then authenticate, review the consent details, select accounts to share, and be redirected back to your application.


Overview

The consent authorization flow involves:

  1. Redirecting the user to the OFP's authorization endpoint
  2. User actions at the OFP (authentication, account selection, consent grant)
  3. Callback to your application with either success or error information
  4. Handling the response in your application

This process follows the OAuth 2.0 authorization code flow with PKCE security.


Step 1: Redirect User to Authorization Endpoint

Building the Authorization URL

FAPI 2.0 requires Pushed Authorization Requests (PAR), which means authorization request details are already submitted to the server. The authorization endpoint call is therefore minimal - only two parameters:

GET {{authorization_endpoint}}?client_id={{CLIENT_ID}}&request_uri={{REQUEST_URI}}

Parameters:

  • client_id: Your registered OIDC client ID
  • request_uri: The URN received from the PAR endpoint (e.g., urn:ietf:params:oauth:request_uri:550e8400-e29b-41d4-a716-446655440000)

Note: response_type and response_mode are NOT included here - they're already in the JAR you submitted to the PAR endpoint.

Example Redirect (Node.js)

const authorizationUrl = new URL('{{authorization_endpoint}}');
authorizationUrl.searchParams.append('client_id', '{{CLIENT_ID}}');
authorizationUrl.searchParams.append('request_uri', requestUri);  // from PAR response

// Redirect user browser
res.redirect(authorizationUrl.toString());

Example Redirect (Python)

from urllib.parse import urlencode

params = {
    'client_id': '{{CLIENT_ID}}',
    'request_uri': request_uri,  # from PAR response
}

authorization_url = f"{{authorization_endpoint}}?{urlencode(params)}"

# Redirect user browser
return redirect(authorization_url)

Step 2: What Happens at the OFP

User Experience During Authorization

When the user is redirected to the OFP, the following occurs:

1. Provider Selection (if needed)

  • If you did not specify dp_id in your consent request, the OFP presents the user with a list of available Data Providers
  • The user selects which institution (bank, fintech, etc.) they want to connect to
  • If you specified dp_id, this step is skipped and the user proceeds directly to authentication

2. User Authentication

  • The user is redirected to their selected Data Provider's login portal
  • The user authenticates with their credentials (username, password, 2FA, etc.)
  • The Data Provider confirms their identity to the OFP
  • The user is presented with:
    • A list of their accounts at the Data Provider
    • The specific consent purpose and data types being requested (from your consent request)
    • The expiration date of the consent
  • The user selects which accounts they wish to share with your application
  • The user reviews the permissions and either grants or denies consent

4. Redirect Back to Your Application

  • After the user grants (or denies) consent, the OFP redirects the user's browser to your registered redirect_uri
  • The redirect includes either:
    • Success: An authorization code (a short-lived token)
    • Error: An error code explaining why authorization failed

Step 3: Handle the Redirect Response

Success Scenario

When the user grants consent, the OFP redirects to your redirect_uri with an authorization code:

https://your-app.com/oauth/callback?code=eyJhbGci...&state=550e8400-e29b-41d4-a716-446655440000

Response Parameters:

  • code: Authorization code (typically a short-lived token, valid for ~10 minutes)
  • state: Your original state value from the JAR request (verify this matches)

Your Application Should:

  1. Verify the state parameter matches what you stored during Step 2 of Creating a Consent
    • Prevents CSRF attacks if state doesn't match, reject the request
  2. Extract the code parameter
  3. Exchange the code for tokens using the token endpoint (see Section 8: Token Exchange)
  4. Redirect user to a success page in your application

Error Scenario

If the user denies consent or an error occurs, the OFP redirects with an error:

https://your-app.com/oauth/callback?error=access_denied&error_description=User+denied+the+consent&state=550e8400-e29b-41d4-a716-446655440000

Error Response Parameters:

  • error: Error code (e.g., access_denied, invalid_request, server_error)
  • error_description: Human-readable description of the error (URL-encoded)
  • state: Your original state value

Common Error Codes:

  • access_denied: User explicitly denied consent
  • invalid_request: Request was invalid (missing parameters, expired PAR request)
  • server_error: Error at OFP or Data Provider
  • temporarily_unavailable: Temporarily unable to process request

Your Application Should:

  1. Verify the state parameter (same as success scenario)
  2. Log the error for debugging
  3. Display appropriate message to the user (e.g., "Consent was not granted. Please try again.")
  4. Optionally retry or offer the user an alternative action

Example Response Handler (Node.js)

app.get('/oauth/callback', (req, res) => {
  const { code, error, error_description, state } = req.query;

  // Verify state
  const storedState = req.session.state;  // stored during Step 2
  if (state !== storedState) {
    return res.status(400).send('State mismatch - possible CSRF attack');
  }

  // Handle error
  if (error) {
    console.error(`Authorization error: ${error} - ${error_description}`);
    return res.status(400).send(`Authorization failed: ${error_description}`);
  }

  // Handle success
  if (code) {
    // Exchange code for tokens (see Section 8)
    exchangeAuthorizationCode(code)
      .then(tokens => {
        // Store tokens and redirect to success page
        req.session.accessToken = tokens.access_token;
        req.session.idToken = tokens.id_token;
        res.redirect('/dashboard');
      })
      .catch(err => {
        console.error('Token exchange failed:', err);
        res.status(500).send('Token exchange failed');
      });
  }
});

Example Response Handler (Python)

from flask import request, redirect, session

@app.route('/oauth/callback')
def oauth_callback():
    code = request.args.get('code')
    error = request.args.get('error')
    error_description = request.args.get('error_description')
    state = request.args.get('state')

    # Verify state
    stored_state = session.get('state')
    if state != stored_state:
        return 'State mismatch - possible CSRF attack', 400

    # Handle error
    if error:
        app.logger.error(f'Authorization error: {error} - {error_description}')
        return f'Authorization failed: {error_description}', 400

    # Handle success
    if code:
        try:
            # Exchange code for tokens (see Section 8)
            tokens = exchange_authorization_code(code)
            
            # Store tokens
            session['access_token'] = tokens['access_token']
            session['id_token'] = tokens['id_token']
            
            return redirect('/dashboard')
        except Exception as e:
            app.logger.error(f'Token exchange failed: {e}')
            return 'Token exchange failed', 500

Important: No Redirect Scenario

In rare situations, the OFP may not redirect back to your application at all. This can occur due to:

Technical Issues:

  • Network failures during the redirect
  • User closes browser before redirect completes
  • Server-side issues at OFP or Data Provider
  • Timeout - User takes too long to complete authorization

Security Issue:

  • Malformed or invalid authorization request - If the authorization request contains incorrectly formed parameters (e.g., invalid JAR signature, expired request_uri, tampered claims), the OFP considers the request potentially malicious. For safety, it will NOT redirect back, as redirecting an attacker to your registered redirect_uri could enable further attacks. Instead, the user sees an error page at the OFP.

What to do:

  • Implement a timeout mechanism in your application (typically 10-15 minutes)
  • If the user hasn't returned within the timeout, prompt them to retry the consent flow
  • Verify your authorization request is correctly formed:
    • JAR signature is valid (PS256)
    • request_uri hasn't expired (PAR endpoint returns 600 second TTL)
    • All claims in JAR are valid and not tampered
  • Provide feedback to users: "Authorization did not complete. Please try again or contact support."

Step 4: Store Important Values

Before proceeding to token exchange, securely store:

  1. Authorization code - Needed for immediate token exchange
  2. request_uri - May be needed for debugging or refreshing consent
  3. state - Keep until verified in callback
  4. code_verifier - Needed for token exchange (PKCE)

These values should be tied to a user session and cleared after successful token exchange.


Next Steps

Once you have the authorization code:



Powered by ozoneapi

© 2026 Open Finance Malaysia Developer Portal. All rights reserved.