Back to Technology

Complete Protocols Master Part 15: Authentication Protocols

January 31, 2026 Wasil Zafar 42 min read

Master identity protocols: OAuth 2.0 for authorization, OpenID Connect for identity, SAML for enterprise, and Kerberos for domain authentication.

Table of Contents

  1. Introduction
  2. OAuth 2.0
  3. OpenID Connect
  4. SAML 2.0
  5. Kerberos
  6. LDAP
  7. Comparison
  8. Summary

Introduction: Authentication vs Authorization

Authentication and authorization are distinct but related concepts. Authentication verifies WHO you are. Authorization determines WHAT you can do.

Series Context: This is Part 15 of 20 in the Complete Protocols Master series. Authentication protocols operate at the Application Layer (Layer 7).
Concepts

Authentication vs Authorization

Authentication vs Authorization:

AUTHENTICATION (AuthN)
"Who are you?"
• Verifies identity
• Username/password
• Certificates
• Biometrics

AUTHORIZATION (AuthZ)
"What can you access?"
• Permissions
• Roles
• Access control
• Scopes

Example - Hotel:
• AuthN: Show ID at check-in (prove identity)
• AuthZ: Key card grants room access (not other rooms)

Protocol Mapping:
• OAuth 2.0: Authorization only
• OpenID Connect: Authentication (built on OAuth)
• SAML: Both (primarily AuthN)
• Kerberos: Both (tickets grant access)
• LDAP: Directory (stores identity data)
Overview

Protocol Landscape

ProtocolTypeBest ForToken Format
OAuth 2.0AuthorizationAPI access, mobileOpaque/JWT
OIDCAuthN + AuthZWeb login, SSOJWT (ID Token)
SAML 2.0AuthN + AuthZEnterprise SSOXML Assertion
KerberosAuthN + AuthZActive DirectoryTickets
LDAPDirectoryUser lookupN/A

OAuth 2.0: Authorization Framework

OAuth 2.0 is THE authorization standard for APIs. It lets users grant apps limited access without sharing credentials. "Log in with Google" uses OAuth.

Key Insight: OAuth 2.0 is about AUTHORIZATION, not authentication. It answers "Can this app access my photos?" not "Who is this user?"
Roles

OAuth 2.0 Actors

OAuth 2.0 Roles:

1. RESOURCE OWNER
   The user who owns the data
   Example: You

2. CLIENT
   The application wanting access
   Example: Third-party photo app

3. AUTHORIZATION SERVER
   Issues tokens after user consent
   Example: Google's auth server

4. RESOURCE SERVER
   Hosts the protected data
   Example: Google Photos API

Flow:
User → "I want to use PhotoApp"
PhotoApp → "Please authorize at Google"
User → Google: "Yes, allow read-only photos"
Google → PhotoApp: "Here's an access token"
PhotoApp → Google Photos API: "Token: xyz, get photos"
API → PhotoApp: "Here are the photos"
Grant Types

OAuth 2.0 Flows

OAuth 2.0 Grant Types:

1. AUTHORIZATION CODE (most secure)
   Best for: Server-side web apps
   Flow: Code exchanged for token server-side
   
2. AUTHORIZATION CODE + PKCE
   Best for: Mobile apps, SPAs
   Flow: Code + code_verifier for security
   PKCE = Proof Key for Code Exchange

3. CLIENT CREDENTIALS
   Best for: Machine-to-machine (no user)
   Flow: App authenticates directly

4. DEVICE CODE
   Best for: TVs, CLI tools (no browser)
   Flow: User authorizes on separate device

5. IMPLICIT (deprecated)
   Was for: SPAs (replaced by PKCE)
   Issue: Token in URL fragment
# Authorization Code Flow with PKCE

# Step 1: Generate code verifier and challenge
code_verifier="random-43-to-128-character-string"
code_challenge=$(echo -n "$code_verifier" | sha256sum | cut -d' ' -f1 | xxd -r -p | base64 -w0 | tr '+/' '-_' | tr -d '=')

# Step 2: Redirect user to authorization
https://auth.example.com/authorize?
  client_id=my-app&
  response_type=code&
  redirect_uri=https://myapp.com/callback&
  scope=read:photos&
  state=random-csrf-token&
  code_challenge=$code_challenge&
  code_challenge_method=S256

# Step 3: User logs in and consents
# Redirected back with authorization code

# Step 4: Exchange code for tokens (server-side)
curl -X POST https://auth.example.com/token \
  -d "grant_type=authorization_code" \
  -d "code=AUTHORIZATION_CODE" \
  -d "redirect_uri=https://myapp.com/callback" \
  -d "client_id=my-app" \
  -d "code_verifier=$code_verifier"

# Response:
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2g..."
}
# OAuth 2.0 Client Credentials Flow (Python)

import requests
import base64

def client_credentials_flow():
    """Machine-to-machine authentication"""
    
    client_id = "my-service"
    client_secret = "super-secret"
    token_url = "https://auth.example.com/token"
    
    # Request access token
    response = requests.post(
        token_url,
        data={
            "grant_type": "client_credentials",
            "scope": "api:read api:write"
        },
        auth=(client_id, client_secret)
    )
    
    tokens = response.json()
    access_token = tokens["access_token"]
    
    print(f"Access Token: {access_token[:50]}...")
    print(f"Expires in: {tokens['expires_in']} seconds")
    
    # Use token to call API
    api_response = requests.get(
        "https://api.example.com/data",
        headers={"Authorization": f"Bearer {access_token}"}
    )
    
    return api_response.json()

# Token structure (JWT)
print("""
JWT Structure:
HEADER.PAYLOAD.SIGNATURE

Header: {"alg": "RS256", "typ": "JWT"}
Payload: {
  "iss": "https://auth.example.com",
  "sub": "user123",
  "aud": "api.example.com",
  "exp": 1735689600,
  "scope": "read:photos"
}
Signature: RSASHA256(header + payload, private_key)
""")

OpenID Connect: Identity Layer

OpenID Connect (OIDC) adds authentication to OAuth 2.0. It provides an ID Token that proves who the user is—not just what they can access.

OIDC

OIDC vs OAuth 2.0

OIDC = OAuth 2.0 + Identity

What OIDC Adds:
• ID Token (JWT proving identity)
• UserInfo endpoint
• Standard claims (name, email, picture)
• Discovery endpoint

Tokens in OIDC:
• Access Token: API authorization (same as OAuth)
• ID Token: User identity (NEW in OIDC)
• Refresh Token: Get new tokens (same as OAuth)

ID Token Claims:
{
  "iss": "https://accounts.google.com",
  "sub": "110169484474386276334",  // Unique user ID
  "aud": "my-client-id",
  "exp": 1735689600,
  "iat": 1735686000,
  "name": "Jane Doe",
  "email": "jane@example.com",
  "picture": "https://..."
}
# OIDC Discovery Endpoint

curl https://accounts.google.com/.well-known/openid-configuration

# Returns:
{
  "issuer": "https://accounts.google.com",
  "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
  "token_endpoint": "https://oauth2.googleapis.com/token",
  "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
  "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
  "scopes_supported": ["openid", "email", "profile"],
  "response_types_supported": ["code", "token", "id_token"],
  "claims_supported": ["sub", "name", "email", "picture"]
}
# OIDC Authentication (Python with Authlib)

def oidc_example():
    """Demonstrate OIDC authentication"""
    
    print("OIDC Authentication Flow")
    print("=" * 50)
    
    print("""
    # Flask OIDC Example
    
    from authlib.integrations.flask_client import OAuth
    
    oauth = OAuth(app)
    oauth.register(
        name='google',
        client_id='your-client-id',
        client_secret='your-client-secret',
        server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
        client_kwargs={'scope': 'openid email profile'}
    )
    
    @app.route('/login')
    def login():
        redirect_uri = url_for('callback', _external=True)
        return oauth.google.authorize_redirect(redirect_uri)
    
    @app.route('/callback')
    def callback():
        token = oauth.google.authorize_access_token()
        
        # ID Token contains user identity
        id_token = token['id_token']
        userinfo = token['userinfo']
        
        # Now you know WHO the user is
        user_id = userinfo['sub']
        email = userinfo['email']
        name = userinfo['name']
        
        return f'Hello {name}!'
    """)
    
    print("\nKey OIDC Scopes:")
    print("• openid: Required, returns ID token")
    print("• profile: Name, picture, etc.")
    print("• email: Email address")
    print("• address: Physical address")
    print("• phone: Phone number")

oidc_example()

SAML 2.0: Enterprise SSO

SAML (Security Assertion Markup Language) is the enterprise standard for Single Sign-On. XML-based, mature, used by most corporate identity providers.

SAML

SAML Components

SAML 2.0 Components:

1. IDENTITY PROVIDER (IdP)
   • Authenticates users
   • Issues SAML assertions
   • Examples: Okta, Azure AD, ADFS

2. SERVICE PROVIDER (SP)
   • Your application
   • Trusts the IdP
   • Consumes assertions

3. SAML ASSERTION
   • XML document proving identity
   • Contains attributes (email, groups)
   • Digitally signed by IdP

SAML Flows:
• SP-Initiated: User starts at app, redirected to IdP
• IdP-Initiated: User starts at IdP portal

SP-Initiated Flow:
User → SP: "Access app"
SP → User: "Redirect to IdP"
User → IdP: "Login page"
User → IdP: "Credentials"
IdP → User: "SAML Response (redirect to SP)"
User → SP: "Here's SAML Response"
SP → User: "Authenticated! Welcome"
# SAML Response Structure (simplified)

<samlp:Response>
  <Issuer>https://idp.example.com</Issuer>
  <Status>
    <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </Status>
  
  <Assertion>
    <Issuer>https://idp.example.com</Issuer>
    
    <Subject>
      <NameID>user@example.com</NameID>
    </Subject>
    
    <Conditions NotBefore="2026-01-31T00:00:00Z" 
                NotOnOrAfter="2026-01-31T00:05:00Z">
      <AudienceRestriction>
        <Audience>https://sp.example.com</Audience>
      </AudienceRestriction>
    </Conditions>
    
    <AttributeStatement>
      <Attribute Name="email">
        <AttributeValue>user@example.com</AttributeValue>
      </Attribute>
      <Attribute Name="groups">
        <AttributeValue>admin</AttributeValue>
        <AttributeValue>developers</AttributeValue>
      </Attribute>
    </AttributeStatement>
    
    <Signature>...</Signature>
  </Assertion>
</samlp:Response>
# SAML Service Provider (Python with python-saml)

def saml_sp_example():
    """Demonstrate SAML Service Provider"""
    
    print("SAML Service Provider Configuration")
    print("=" * 50)
    
    print("""
    # Flask SAML Example (using python3-saml)
    
    from onelogin.saml2.auth import OneLogin_Saml2_Auth
    
    @app.route('/saml/login')
    def saml_login():
        req = prepare_flask_request(request)
        auth = OneLogin_Saml2_Auth(req, custom_base_path=SAML_PATH)
        return redirect(auth.login())
    
    @app.route('/saml/acs', methods=['POST'])
    def saml_acs():
        # Assertion Consumer Service - receives SAML Response
        req = prepare_flask_request(request)
        auth = OneLogin_Saml2_Auth(req, custom_base_path=SAML_PATH)
        
        auth.process_response()
        errors = auth.get_errors()
        
        if not errors:
            # Authentication successful
            attributes = auth.get_attributes()
            name_id = auth.get_nameid()
            
            user_email = attributes.get('email', [None])[0]
            user_groups = attributes.get('groups', [])
            
            # Create session
            session['user'] = {
                'email': user_email,
                'groups': user_groups
            }
            
            return redirect('/dashboard')
        else:
            return f'SAML Error: {errors}', 400
    """)
    
    print("\nSAML Metadata Exchange:")
    print("• SP publishes metadata (entity ID, ACS URL, cert)")
    print("• IdP publishes metadata (SSO URL, signing cert)")
    print("• Both import each other's metadata")

saml_sp_example()

Kerberos: Network Authentication

Kerberos is the authentication backbone of Active Directory. Uses tickets instead of passwords—users authenticate once, get tickets for services.

Kerberos

Kerberos Architecture

Kerberos Components:

1. KDC (Key Distribution Center)
   • Authentication Server (AS)
   • Ticket Granting Server (TGS)
   • Holds all secrets

2. TICKETS
   • TGT (Ticket Granting Ticket): Proves identity
   • Service Ticket: Grants access to specific service

3. PRINCIPALS
   • Users: user@REALM.COM
   • Services: HTTP/web.realm.com@REALM.COM

Kerberos Authentication:
User → AS: "I'm alice, want TGT"
AS → User: "Here's TGT (encrypted)"
User → TGS: "TGT, want ticket for fileserver"
TGS → User: "Here's service ticket"
User → FileServer: "Service ticket"
FileServer → User: "Access granted"

Key Concept: User password NEVER sent over network
# Kerberos ticket management (Linux)

# Initialize (get TGT)
kinit alice@EXAMPLE.COM
# Enter password (locally hashed, not sent)

# List tickets
klist
# Ticket cache: FILE:/tmp/krb5cc_1000
# Default principal: alice@EXAMPLE.COM
# 
# Valid starting       Expires              Service principal
# 01/31/2026 09:00    01/31/2026 19:00     krbtgt/EXAMPLE.COM@EXAMPLE.COM

# Get service ticket (automatic when accessing service)
curl --negotiate -u : http://web.example.com/

# Destroy tickets (logout)
kdestroy

# Check keytab (for service accounts)
klist -k /etc/krb5.keytab
# Kerberos authentication in Python

def kerberos_example():
    """Kerberos authentication demonstration"""
    
    print("Kerberos in Python")
    print("=" * 50)
    
    print("""
    # Using requests-kerberos
    
    import requests
    from requests_kerberos import HTTPKerberosAuth
    
    # Auto-use cached TGT
    response = requests.get(
        'https://api.example.com/data',
        auth=HTTPKerberosAuth()
    )
    
    # For SPNEGO (HTTP Negotiate)
    from requests_kerberos import HTTPKerberosAuth, OPTIONAL
    
    response = requests.get(
        'https://api.example.com/data',
        auth=HTTPKerberosAuth(mutual_authentication=OPTIONAL)
    )
    """)
    
    print("\nKerberos vs Password Auth:")
    print("• Kerberos: Ticket-based, password never leaves client")
    print("• Password: Sent to server (even if encrypted)")
    print("• Kerberos: Mutual auth (server proves identity too)")
    print("• Kerberos: Time-limited tickets, auto-expire")

kerberos_example()

LDAP: Directory Services

LDAP (Lightweight Directory Access Protocol) stores and retrieves identity data. It's not strictly an authentication protocol—it's the database that backs authentication.

LDAP

LDAP Structure

LDAP Concepts:

1. DIRECTORY TREE (DIT)
   Hierarchical structure
   dc=example,dc=com (domain)
    └── ou=People
         └── uid=alice
         └── uid=bob
    └── ou=Groups
         └── cn=developers

2. DISTINGUISHED NAME (DN)
   Unique identifier for entry
   uid=alice,ou=People,dc=example,dc=com

3. ATTRIBUTES
   cn: Common Name
   uid: User ID
   mail: Email
   userPassword: Hashed password
   memberOf: Group membership

4. OPERATIONS
   • Bind: Authenticate
   • Search: Query
   • Add/Modify/Delete: CRUD
# LDAP queries with ldapsearch

# Anonymous search (if allowed)
ldapsearch -x -H ldap://ldap.example.com \
  -b "dc=example,dc=com" "(uid=alice)"

# Authenticated search
ldapsearch -x -H ldap://ldap.example.com \
  -D "cn=admin,dc=example,dc=com" -W \
  -b "ou=People,dc=example,dc=com" "(objectClass=person)"

# Search specific attributes
ldapsearch -x -H ldap://ldap.example.com \
  -b "dc=example,dc=com" "(uid=alice)" cn mail memberOf

# Test authentication (bind)
ldapwhoami -x -H ldap://ldap.example.com \
  -D "uid=alice,ou=People,dc=example,dc=com" -W
# LDAP authentication in Python

import ldap3

def ldap_auth(username, password):
    """Authenticate user against LDAP"""
    
    server = ldap3.Server('ldap://ldap.example.com', get_info=ldap3.ALL)
    user_dn = f"uid={username},ou=People,dc=example,dc=com"
    
    try:
        conn = ldap3.Connection(server, user_dn, password, auto_bind=True)
        print(f"✅ Authentication successful for {username}")
        
        # Search for user attributes
        conn.search(
            user_dn,
            '(objectClass=person)',
            attributes=['cn', 'mail', 'memberOf']
        )
        
        if conn.entries:
            user = conn.entries[0]
            print(f"   Name: {user.cn}")
            print(f"   Email: {user.mail}")
            print(f"   Groups: {user.memberOf}")
        
        conn.unbind()
        return True
        
    except ldap3.core.exceptions.LDAPBindError:
        print(f"❌ Authentication failed for {username}")
        return False

# Example
# ldap_auth('alice', 'password123')

Protocol Comparison

Decision Guide

Choosing Authentication Protocol

ScenarioBest ChoiceReason
Modern web appOIDCIndustry standard, JWT tokens
Mobile appOAuth 2.0 + PKCESecure, no client secret
Enterprise SSOSAMLMature, wide support
Windows domainKerberosBuilt into AD
API authorizationOAuth 2.0Scoped access tokens
Machine-to-machineOAuth 2.0 CCNo user involved
OIDC vs SAML

When to Use Which

OIDC vs SAML:

OIDC (Modern):
✅ JSON/JWT (lightweight)
✅ Mobile-friendly
✅ REST APIs
✅ Modern IdPs (Auth0, Okta)
✅ Easier to implement

SAML (Enterprise):
✅ Mature, battle-tested
✅ Wide enterprise support
✅ Complex attribute mapping
✅ Legacy system integration
❌ XML (verbose)
❌ Complex to implement

Choose OIDC for:
• New applications
• Mobile apps
• API-first architecture
• Consumer applications

Choose SAML for:
• Enterprise with existing SAML IdP
• Legacy system integration
• Complex attribute requirements
• Regulatory compliance

Summary & Next Steps

Key Takeaways:
  • OAuth 2.0: Authorization framework for API access
  • OIDC: Identity layer on OAuth, provides ID tokens
  • SAML: Enterprise SSO with XML assertions
  • Kerberos: Ticket-based auth for AD domains
  • LDAP: Directory for storing identity data
Quiz

Test Your Knowledge

  1. OAuth 2.0 vs OIDC? (AuthZ vs AuthN)
  2. What is PKCE? (Proof Key for Code Exchange, mobile security)
  3. SAML vs OIDC token format? (XML vs JWT)
  4. What's a TGT in Kerberos? (Ticket Granting Ticket)
  5. LDAP purpose? (Directory storage, identity lookup)

Next in the Series

In Part 16: Network Management Protocols, we'll explore SNMP, NetFlow, and syslog for monitoring and managing networks.