Skip to content

OAuth2 Consent — User Consent Management in Authorization Code Flows

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about OAuth2 Consent. We cover key concepts, practical examples, and best practices to help you master this topic.

OAuth2 consent is the Process where a resource owner explicitly authorizes a client to access their protected resources, choosing which scopes to grant and reviewing the client's identity before approving access.

What You'll Learn

  • Consent screen design and user experience
  • Dynamic consent for new or additional scopes
  • Remembered consent and consent revocation
  • Implementing consent in your authorization server
  • Security considerations for consent flows

Why It Matters

Consent is the core user protection in OAuth2. A poorly designed consent screen leads to users approving malicious clients or denying legitimate ones. DodaTech's authorization server presents clear, actionable consent screens that reduced accidental deny rates by 60% and improved approval rates for legitimate apps.

Real-World Use

A security dashboard app requests read access to threat reports and write access to remediation actions. The consent screen shows the app name, publisher (verified by DodaTech), requested scopes with plain-language explanations, and the data each scope accesses.

sequenceDiagram
    participant User as Resource Owner
    participant Browser as Browser
    participant Auth as Authorization Server
    participant Client as Third-Party App

    Client->>Auth: Authorization Request (client_id, redirect_uri, scope)
    Auth->>Browser: Redirect to consent page
    Browser->>User: Show consent screen: "App X wants access to..."

    Note over User,Browser: App Name, Publisher, Scopes (with descriptions)

    User->>Browser: Approve (select scopes)
    Browser->>Auth: POST consent approval
    Auth->>Auth: Store consent grant
    Auth->>Browser: Redirect with authorization code
    Browser->>Client: Authorization code
    Client->>Auth: Exchange code for tokens

Code Examples

from flask import Flask, request, render_template, redirect, session
import uuid

app = Flask(__name__)

@app.route('/oauth/authorize', methods=['GET'])
def authorize():
    """Show consent screen to user."""
    client_id = request.args.get('client_id')
    scope = request.args.get('scope', '').split()
    redirect_uri = request.args.get('redirect_uri')
    state = request.args.get('state')

    client = get_client(client_id)
    scope_descriptions = get_scope_descriptions(scope)
    user = get_current_user()

    # Check for previously remembered consent
    previous_consent = get_consent(user.id, client_id)
    if previous_consent and set(scope).issubset(set(previous_consent['scopes'])):
        # Auto-approve if previously consented
        return auto_approve(client, user, scope, redirect_uri, state)

    return render_template('consent.html',
        client_name=client.name,
        client_logo=client.logo_url,
        publisher=client.publisher,
        scope_descriptions=scope_descriptions,
        requested_scopes=scope,
        redirect_uri=redirect_uri,
        state=state,
        client_id=client_id
    )

def get_scope_descriptions(scopes):
    descriptions = {
        'read:threats': 'View threat reports and indicators',
        'write:threats': 'Create and update threat reports',
        'read:remediation': 'View remediation guides',
        'write:remediation': 'Execute remediation actions',
        'read:users': 'View user profiles',
        'admin': 'Full administrative access'
    }
    return [{'scope': s, 'description': descriptions.get(s, s)}
            for s in scopes]
@app.route('/oauth/consent', methods=['POST'])
def process_consent():
    """Process user consent decision."""
    user = get_current_user()
    client_id = request.form.get('client_id')
    approved_scopes = request.form.getlist('scopes')
    remember = 'remember' in request.form
    decision = request.form.get('decision')

    if decision == 'deny':
        return redirect_with_error('access_denied')

    # Record consent
    consent_record = {
        'user_id': user.id,
        'client_id': client_id,
        'scopes': approved_scopes,
        'granted_at': datetime.now(timezone.utc).isoformat(),
        'remember': remember
    }
    save_consent(consent_record)

    # Generate authorization code
    auth_code = str(uuid.uuid4())
    save_auth_code(auth_code, {
        'client_id': client_id,
        'user_id': user.id,
        'scopes': approved_scopes,
        'redirect_uri': request.form.get('redirect_uri'),
        'expires_at': datetime.now(timezone.utc) + timedelta(minutes=5)
    })

    # Redirect to client
    redirect_uri = request.form.get('redirect_uri')
    state = request.form.get('state')
    return redirect(f"{redirect_uri}?code={auth_code}&state={state}")

def redirect_with_error(error):
    redirect_uri = request.form.get('redirect_uri')
    state = request.form.get('state')
    return redirect(f"{redirect_uri}?error={error}&state={state}")
@app.route('/oauth/consent/revoke', methods=['POST'])
def revoke_consent():
    """User revokes previously granted consent."""
    user = get_current_user()
    client_id = request.form.get('client_id')
    scope = request.form.getlist('scopes')

    if scope:
        # Revoke specific scopes only
        revoke_scopes(user.id, client_id, scope)
        print(f"Revoked scopes for client {client_id}: {scope}")
    else:
        # Revoke all consent for this client
        revoke_all_consent(user.id, client_id)
        print(f"Revoked all consent for client {client_id}")

    return render_template('consent-revoked.html',
        client_name=get_client(client_id).name)

@app.route('/oauth/consent/manage')
def manage_consent():
    """Show all clients the user has consented to."""
    user = get_current_user()
    consents = get_user_consents(user.id)

    return render_template('consent-manage.html',
        consents=[{
            'client': get_client(c['client_id']),
            'scopes': c['scopes'],
            'granted_at': c['granted_at']
        } for c in consents]
    )

Common Mistakes

1. Burying Scopes in Legalese

Show scopes in plain language with concrete examples of what each scope allows.

2. Not Showing the Client Identity

Display the client name, publisher, website, and logo so users can verify legitimacy.

When using remembered consent, still show a notification about what scopes the client is using.

Users must be able to review and revoke consents from a settings page.

5. Ignoring Incremental Authorization

When a client requests new scopes, prompt for consent again even if previous consent exists.

Practice Questions

  1. What information should a consent screen display?
  2. How does remembered consent work?
  3. What is incremental authorization?
  4. How do users revoke consent?
  5. Why is showing the client identity important?

Answers:

  1. Client name, publisher, logo, requested scopes with plain-language descriptions, and the redirect URI.
  2. The authorization server stores the user's consent decision and auto-approves future requests with the same or subset of scopes.
  3. The client starts with a minimal scope set and requests additional scopes later, triggering a new consent prompt.
  4. Through a consent management page where users can review all granted consents and revoke them individually.
  5. Attackers create lookalike apps. Verified publisher identity and logos help users distinguish legitimate apps.

Challenge: Build a consent management system with remembered consent, incremental authorization, scope descriptions, and a consent revocation dashboard.

FAQ

Can a consent screen be skipped?

: Yes, for first-party apps (same developer as the authorization server). Third-party apps must show consent.

What is pairwise pseudonymous identifiers?

: The authorization server issues a different sub claim per client, preventing clients from correlating users.

How long does remembered consent last?

: Until revoked or the user changes their password. Some implementations expire consent after 90-180 days.

Can I customize the consent screen per client?

: Yes. Clients can provide a logo, privacy policy URL, and terms of service URL that are displayed on the consent screen.

What happens when scopes change?

: If the client requests scopes not previously consented, the authorization server must prompt for consent again.

What's Next

Implement consent in your {{< ilink "OAuth" "OAuth2 Authorization Server" }}, then explore {{< ilink "OAuth" "OAuth2 Scopes" }} for fine-grained permission design.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro