Wednesday, January 15, 2025

Refresh Token Flow - PingFederate

When using PingFederate with rolling tokens enabled, refresh tokens are managed to enhance security by invalidating previously issued refresh tokens after they are used. This ensures that only one valid refresh token exists at a time. 

Here's how you can use a refresh token with PingFederate in such a scenario:


Step-by-Step Guide

1. Understand Rolling Tokens

  • With rolling tokens enabled, every time a refresh token is used to obtain a new access token, PingFederate issues:
    • A new refresh token (replacing the old one).
    • A new access token.
  • The previously issued refresh token becomes invalid, so the client must always use the latest refresh token received.

2. Enable Rolling Tokens in PingFederate

  • In PingFederate, rolling refresh tokens are configured at the OAuth Client level.
  • To enable rolling tokens:
    • Go to OAuth Settings for the specific client in PingFederate Admin Console.
    • Under Refresh Token Settings, select:
      • Enable rolling tokens: Ensures old refresh tokens are invalidated after use.
      • Revocation policy: Determines if older refresh tokens are immediately revoked or if they expire naturally.

3. Use the Refresh Token to Obtain New Tokens

  • When the access token expires, the client application must send the refresh token to the Token Endpoint to obtain a new set of tokens.

Token Endpoint Request:


POST /as/token.oauth2 HTTP/1.1 Host: <pingfederate-server> Content-Type: application/x-www-form-urlencoded grant_type=refresh_token& refresh_token=<current_refresh_token>& client_id=<client_id>& client_secret=<client_secret>

Parameters:

  • grant_type: Set to refresh_token.
  • refresh_token: The most recent refresh token issued by PingFederate.
  • client_id: The client ID of your application.
  • client_secret: The client secret associated with the client (if using client authentication).

4. Parse the Token Response

  • After a successful refresh request, PingFederate will respond with:

    { "access_token": "<new_access_token>", "refresh_token": "<new_refresh_token>", "expires_in": 3600, "token_type": "Bearer" }
  • Important: Save the new refresh token (<new_refresh_token>). This is now the only valid refresh token for future requests.

5. Handle Errors Gracefully

  • If the refresh token is invalid (e.g., due to being used already or expired), PingFederate will return an error:

    { "error": "invalid_grant", "error_description": "The refresh token is invalid." }
  • Resolution:
    • Prompt the user to reauthenticate to obtain a new set of tokens.
    • Ensure the application always uses the latest refresh token received.

6. Secure Token Storage

  • Since refresh tokens are sensitive, store them securely:
    • Use encrypted storage (e.g., secure server-side database).
    • Never expose refresh tokens in client-side environments (e.g., browser or local storage).

Example Workflow

  1. Initial Login:

    • User authenticates, and PingFederate issues:
      • Access Token (e.g., valid for 1 hour).
      • Refresh Token (e.g., valid for 7 days).
  2. Access Token Expiry:

    • After the access token expires, the client uses the refresh token to obtain a new access token and refresh token.
  3. Refresh Token Usage:

    • The client sends the current refresh token to PingFederate's Token Endpoint.
    • PingFederate responds with a new access token and a new refresh token.
    • The client replaces the old refresh token with the new one.
  4. Token Revocation:

    • If the refresh token is compromised or misused, rolling tokens ensure the impact is limited since only the latest token is valid.

Best Practices for Rolling Tokens

  1. Always Use the Latest Refresh Token:

    • After each token refresh, replace the old refresh token with the new one immediately.
  2. Implement Graceful Error Handling:

    • Handle scenarios where the refresh token is invalid or expired by reauthenticating the user.
  3. Secure Storage:

    • Store refresh tokens securely to prevent unauthorized access.
  4. Short-Lived Access Tokens:

    • Configure short access token lifetimes (e.g., 15–60 minutes) for better security and rely on refresh tokens to maintain session continuity.

Debugging Tips

If refresh token workflows aren't functioning as expected:

  1. Check PingFederate Logs:

    • Review logs in PingFederate Admin Console for error details during token refresh attempts.
  2. Verify Client Configuration:

    • Ensure rolling tokens are enabled in the OAuth client settings.
    • Confirm token lifetimes and scopes are configured correctly.
  3. Monitor Token Usage:

    • Use PingFederate's Token Inspector to validate issued tokens and their status.
  4. Inspect HTTP Requests/Responses:

    • Use tools like Postman or Fiddler to examine the refresh token flow.

Configuring Rolling Refresh Tokens in PingFederate

1. Configure an OAuth Client in PingFederate

  1. Log into the PingFederate Admin Console.
  2. Navigate to Applications → OAuth Clients and select an existing client or create a new one.
  3. In the Client Settings:
    • Enable Refresh Token Grant:
      • Under the Allowed Grant Types, ensure Refresh Token is selected.
    • Configure Rolling Tokens:
      • Navigate to Refresh Token Settings and enable Rolling Refresh Tokens.
      • Set the Token Lifetime (e.g., 7 days or less depending on your security policy).
      • Select Revocation Policy:
        • Immediate Revocation: Revokes the old refresh token as soon as a new one is issued.
        • Non-Revocation: Allows the old refresh token to remain valid until it expires naturally.

2. Configure Token Lifetimes

  1. Go to Authentication Policies → Access Token Manager.
  2. Configure the Access Token Lifetime (e.g., 15–60 minutes for security).
  3. Configure the Refresh Token Lifetime to define how long the refresh token remains valid (e.g., 7 days).

3. Test the Configuration

  • Use a tool like Postman or cURL to simulate the OAuth flow:
    1. Authenticate to get the initial access token and refresh token.
    2. Use the refresh token to request a new access token.
    3. Verify that a new refresh token is issued during the refresh flow.

Testing the Refresh Token Flow

Step 1: Authenticate and Obtain Tokens

Send a request to PingFederate's Authorization Endpoint or Token Endpoint (depending on the grant type).

Example (Authorization Code Grant):



POST /as/token.oauth2 HTTP/1.1 Host: <pingfederate-server> Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=<authorization_code>& redirect_uri=<redirect_uri>& client_id=<client_id>& client_secret=<client_secret>

Response:


{ "access_token": "<access_token>", "refresh_token": "<refresh_token>", "expires_in": 3600, "token_type": "Bearer" }

Step 2: Use the Refresh Token

Send the refresh token to the Token Endpoint to get new tokens:


POST /as/token.oauth2 HTTP/1.1 Host: <pingfederate-server> Content-Type: application/x-www-form-urlencoded grant_type=refresh_token& refresh_token=<current_refresh_token>& client_id=<client_id>& client_secret=<client_secret>

Response (with rolling tokens enabled):


{ "access_token": "<new_access_token>", "refresh_token": "<new_refresh_token>", "expires_in": 3600, "token_type": "Bearer" }
  • Ensure that:
    1. A new refresh token is returned in the response.
    2. The previous refresh token is invalidated.

Troubleshooting Rolling Refresh Token Issues

1. Common Problems

  • Invalid Grant Error:

    • Cause: Using an old/expired refresh token or a refresh token that has already been used.
    • Solution: Always store and use the most recently issued refresh token.
  • Refresh Token Not Rolling:

    • Cause: Rolling tokens may not be enabled for the client.
    • Solution: Verify the Refresh Token Settings in the PingFederate Admin Console for the OAuth client.
  • Token Expired Errors:

    • Cause: Using a refresh token beyond its configured lifetime.
    • Solution: Check the refresh token lifetime in the PingFederate client settings and adjust if needed.
  • Token Revocation Issues:

    • Cause: Old refresh tokens are still being accepted.
    • Solution: Ensure that Immediate Revocation is enabled in the client’s Refresh Token Settings.

2. Debugging Tools

  • PingFederate Logs:

    • Check the pingfederate.log file for errors or warnings related to token issuance and validation.
    • Located in the PingFederate installation directory under log/.
  • Token Inspector:

    • Go to Runtime → OAuth Access Tokens in the PingFederate Admin Console.
    • Use the Token Inspector to verify the status, claims, and expiration times of issued tokens.
  • Postman or cURL:

    • Use these tools to manually test the token exchange flow and ensure the expected tokens are being issued.

3. Validate Client Configuration

  • Ensure the Refresh Token Grant is enabled in the client’s Allowed Grant Types.
  • Verify the redirect_uri and scopes match those used during token requests.

4. Confirm Revocation Policy

  • Navigate to the client’s Refresh Token Settings and confirm the selected Revocation Policy matches your expectations.

5. Enable Debug Logging

  • Increase the logging level in PingFederate to capture detailed information about token exchanges:
    • In the Admin Console, go to Server Settings → Runtime → Diagnostics.
    • Set the logging level to DEBUG.

Best Practices

  1. Always Store the Latest Refresh Token:

    • Replace the old refresh token immediately upon receiving a new one.
  2. Secure Token Storage:

    • Store tokens in encrypted databases or secure server-side storage.
  3. Monitor Token Usage:

    • Use PingFederate's logging and monitoring features to detect misuse or anomalies.
  4. Implement Token Rotation Logic in Clients:

    • Ensure the client application is designed to handle rolling tokens by dynamically updating stored tokens.
  5. Set Appropriate Token Lifetimes:

    • Keep access tokens short-lived for security and rely on refresh tokens for session continuity.

Here’s an example of how to implement a rolling refresh token workflow using PingFederate in a Python client application. This sample demonstrates how to handle token exchange and properly manage refresh tokens.


Python Code Example

Dependencies

Install the required libraries:

pip install requests

Token Management with PingFederate

import requests
import json import time # PingFederate configuration PINGFEDERATE_TOKEN_ENDPOINT = "https://<pingfederate-server>/as/token.oauth2" CLIENT_ID = "<your-client-id>" CLIENT_SECRET = "<your-client-secret>" REDIRECT_URI = "<your-redirect-uri>" REFRESH_TOKEN = None # Initialize with None; this will be updated dynamically. # Global token storage (for demonstration purposes) TOKENS = { "access_token": None, "refresh_token": None, "expires_in": None, "token_acquired_at": None } def authenticate_with_authorization_code(authorization_code): """Authenticate using an authorization code and obtain initial tokens.""" payload = { "grant_type": "authorization_code", "code": authorization_code, "redirect_uri": REDIRECT_URI, "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET } response = requests.post(PINGFEDERATE_TOKEN_ENDPOINT, data=payload) if response.status_code == 200: tokens = response.json() store_tokens(tokens) print("Authenticated successfully!") return tokens else: print("Failed to authenticate:", response.text) return None def refresh_access_token(): """Use the refresh token to obtain a new access token.""" global REFRESH_TOKEN if not REFRESH_TOKEN: print("No refresh token available. User must reauthenticate.") return None payload = { "grant_type": "refresh_token", "refresh_token": REFRESH_TOKEN, "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET } response = requests.post(PINGFEDERATE_TOKEN_ENDPOINT, data=payload) if response.status_code == 200: tokens = response.json() store_tokens(tokens) print("Access token refreshed successfully!") return tokens else: print("Failed to refresh token:", response.text) return None def store_tokens(tokens): """Store tokens securely and update global token storage.""" global REFRESH_TOKEN, TOKENS TOKENS["access_token"] = tokens.get("access_token") TOKENS["refresh_token"] = tokens.get("refresh_token") TOKENS["expires_in"] = tokens.get("expires_in") TOKENS["token_acquired_at"] = time.time() REFRESH_TOKEN = TOKENS["refresh_token"] def is_access_token_expired(): """Check if the access token is expired.""" if not TOKENS["access_token"] or not TOKENS["expires_in"]: return True elapsed_time = time.time() - TOKENS["token_acquired_at"] return elapsed_time > TOKENS["expires_in"] def make_authenticated_request(url, headers=None): """Make an API request using the access token.""" if is_access_token_expired(): print("Access token expired. Refreshing...") if not refresh_access_token(): print("Failed to refresh token. Reauthentication required.") return None headers = headers or {} headers["Authorization"] = f"Bearer {TOKENS['access_token']}" response = requests.get(url, headers=headers) if response.status_code == 401: # Unauthorized (token may have been revoked) print("Token invalid. Attempting to refresh...") if not refresh_access_token(): print("Reauthentication required.") return None return make_authenticated_request(url, headers) # Retry request return response # Example usage if __name__ == "__main__": # Replace with the actual authorization code obtained from PingFederate authorization_code = "<authorization_code>" # Step 1: Authenticate and obtain initial tokens tokens = authenticate_with_authorization_code(authorization_code) if tokens: print("Initial tokens:", tokens) # Step 2: Make an API call using the access token api_url = "https://<api-endpoint>/resource" response = make_authenticated_request(api_url) if response: print("API Response:", response.text) # Step 3: Simulate token expiration and refresh print("Simulating token expiration...") time.sleep(TOKENS["expires_in"] + 1) # Wait until the token expires refreshed_tokens = refresh_access_token() if refreshed_tokens: print("Refreshed tokens:", refreshed_tokens) # Step 4: Make another API call with the refreshed token response = make_authenticated_request(api_url) if response: print("API Response after refresh:", response.text)

How It Works

  1. Authenticate with Authorization Code:

    • Use the authorization code to obtain an initial access token and refresh token.
  2. Use the Refresh Token:

    • Refresh the access token using the most recent refresh token.
  3. Handle Token Expiry:

    • Automatically refresh the access token when it expires.
  4. Token Rotation:

    • Update the stored refresh token whenever a new one is issued (rolling tokens).
  5. API Requests:

    • Attach the access token to API requests and retry if the token is invalid.

Best Practices

  • Secure Storage: Store tokens securely (e.g., encrypted storage or a secure database).
  • Handle Errors Gracefully: Implement retry logic and user reauthentication when needed.
  • Minimize Lifetime: Use short-lived access tokens for security and rely on refresh tokens for continuity.
  • Monitor Token Usage: Log and monitor token usage to detect anomalies.

Access Management Fundamentals - Part 2

 Ok, so leading on from part 1, let's get into some Open ID Connect (OIDC) and JWTs :-)

What is OpenID Connect (OIDC)?

OpenID Connect (OIDC) is an authentication protocol built on top of OAuth 2.0 that allows clients (applications) to verify the identity of users and obtain their basic profile information securely. Unlike OAuth 2.0, which is primarily used for authorization (granting access to resources), OIDC focuses on authentication (confirming who the user is).


Key Components of OIDC

  1. Issuer (Identity Provider, IdP):

    • Authenticates users and issues tokens (ID tokens, access tokens, and optionally refresh tokens).
    • Examples: Azure AD, Google, Okta, Ping Identity.
  2. Client (Relying Party, RP):

    • The application or service that relies on the IdP for user authentication.
  3. Endpoints:

    • Authorization Endpoint: Where users are redirected to log in.
    • Token Endpoint: Issues tokens after successful authentication.
    • UserInfo Endpoint: Provides additional user profile information.
  4. Tokens:

    • ID Token: Contains information about the authenticated user (e.g., user ID, email).
      • Signed using JSON Web Token (JWT) format.
    • Access Token: Grants access to protected resources (optional for SSO).
    • Refresh Token: Allows token renewal without requiring user interaction.

Why Use OIDC for SSO?

  1. Modern and Lightweight:

    • Designed for modern web and mobile apps with REST/JSON APIs.
    • Simpler than legacy protocols like SAML.
  2. Interoperability:

    • Widely supported across cloud platforms, mobile apps, and microservices.
  3. Enhanced Security:

    • Supports modern security mechanisms like PKCE (Proof Key for Code Exchange) and token introspection.
  4. Flexible User Info:

    • Fetch user profile data dynamically via the UserInfo endpoint.
  5. Standardized JWT:

    • ID tokens are easy to parse and validate using standard libraries.

How to Use OIDC to Enable SSO

1. Set Up an Identity Provider (IdP)

  • Use an IdP that supports OIDC, such as:
    • Azure AD
    • Google Identity
    • Okta
    • PingOne
    • AWS Cognito

Steps:

  • Register your application (Client ID and Client Secret will be generated).
  • Configure redirect URIs for your application.
  • Set up scopes (e.g., openid, profile, email) to define what information to share with the client.

2. Integrate the Client Application

  • Configure your application to use the IdP for authentication:
    • Use an OIDC-compatible library or SDK (e.g., libraries for Python, Java, .NET, JavaScript).
  • Specify the endpoints:
    • Authorization Endpoint
    • Token Endpoint
    • UserInfo Endpoint (optional)

3. Authentication Flow

  • Typically, the Authorization Code Flow is used for SSO:
    1. Initiate Login:
      • The user is redirected to the IdP's Authorization Endpoint.
      • Include parameters like response_type=code, scope=openid profile, and redirect_uri.
    2. Authenticate User:
      • The IdP prompts the user to log in.
    3. Return Authorization Code:
      • Upon successful login, the IdP redirects the user back to the client with an authorization code.
    4. Exchange Code for Tokens:
      • The client sends the authorization code to the Token Endpoint and receives:
        • ID Token
        • Access Token (optional)
    5. Validate ID Token:
      • The client verifies the token’s signature, issuer, and expiration.

4. Authorize Access

  • Use the validated ID token to establish a user session in the application.
  • Optionally, use the Access Token to call APIs for additional data or functionality.

5. Implement Role-Based Access Control (RBAC)

  • Use claims in the ID token (e.g., roles, groups) to assign permissions in your application.

Example Use Case: Enabling SSO with OIDC

Scenario: SSO for a Web Application

  1. IdP Configuration:

    • Register the web application in PingOne or Azure AD.
    • Define the redirect URI: https://example.com/callback.
    • Set scopes: openid profile email.
  2. Client Application Configuration:

    • Integrate an OIDC library (e.g., oidc-client for JavaScript, passport-openidconnect for Node.js).
    • Set up endpoints from the IdP:
      • Authorization: https://idp.example.com/authorize
      • Token: https://idp.example.com/token
      • UserInfo: https://idp.example.com/userinfo.
  3. User Flow:

    • User accesses the web app.
    • The app redirects the user to the IdP for authentication.
    • The user logs in, and the IdP redirects them back with an authorization code.
    • The app exchanges the code for tokens and logs the user in.

What is a JWT?

A JWT (JSON Web Token) is a compact, URL-safe, and standardized way to represent claims (statements about an entity, typically the user) in JSON format. It is commonly used for authentication, authorization, and data exchange in modern web applications.

JWTs are widely employed in protocols like OAuth 2.0 and OpenID Connect (OIDC), enabling secure communication between different systems.


Structure of a JWT

A JWT consists of three parts, separated by periods (.):

1. Header (Base64URL-encoded JSON):

  • Contains metadata about the token, including the algorithm used to sign it.
Example:
json { 
  "alg": "HS256",  
   "typ": "JWT"
}

2. Payload (Base64URL-encoded JSON):

  • Contains the claims, which are statements about the user or other entities.
  • Claims can be:
    • Registered Claims: Predefined claims like iss (issuer), sub (subject), exp (expiration time), etc.
    • Public Claims: Custom claims shared across applications.
    • Private Claims: Custom claims specific to your application.
  • Example:
    json
    { "sub": "1234567890", "name": "John Doe", "iat": 1615324337, "exp": 1615327937 }

3. Signature (Base64URL-encoded string):

  • Ensures the integrity and authenticity of the token.
  • Created by encoding the header and payload, then signing it with a secret or private key using the specified algorithm.
  • Example process (using HS256):

    Signature = HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )

Example JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
For decoding JWTs https://jwt.io is your friend :-)

Characteristics of JWT

  1. Compact and URL-Safe:

    • Can be transmitted via URLs, HTTP headers, or cookies without causing encoding issues.
  2. Stateless:

    • Does not require server-side storage, as all data is self-contained within the token.
  3. Secure:

    • Signed to prevent tampering.
    • Can optionally be encrypted for confidentiality (JWE format).
  4. Self-Contained:

    • Includes all the necessary information for authentication or authorization.

Common Use Cases

  1. Authentication:

    • OIDC uses JWTs (ID Tokens) to verify a user's identity.
    • Example: After a user logs in, the server issues a JWT containing user details, which the client includes in subsequent requests.
  2. Authorization:

    • OAuth 2.0 uses JWTs (Access Tokens) to grant clients permission to access resources.
    • Example: A JWT with a claim indicating the user's roles or permissions.
  3. Data Exchange:

    • JWTs are used to securely transmit information between parties, ensuring integrity and optional confidentiality.


How JWTs Work in Authentication

  1. User Logs In:

    • The user submits their credentials to the server.
  2. Server Issues JWT:

    • After validating the credentials, the server generates and signs a JWT containing the user's information.
  3. Client Stores JWT:

    • The client (e.g., browser or mobile app) stores the JWT in a secure location, such as a cookie or local storage.
  4. Client Sends JWT:

    • On subsequent requests, the client includes the JWT (e.g., in the Authorization header).
  5. Server Validates JWT:

    • The server verifies the JWT's signature and checks claims like expiration time (exp) before granting access.

Advantages of JWTs

  1. Scalability:

    • Stateless design reduces server-side storage and makes them ideal for distributed systems.
  2. Interoperability:

    • Standardized format ensures compatibility across platforms and systems.
  3. Efficiency:

    • Compact size reduces overhead for network communication.
  4. Security:

    • Signed and optionally encrypted, ensuring integrity and confidentiality.

Challenges and Considerations

  1. Token Revocation:

    • Stateless design means JWTs cannot be revoked once issued. Use short expiration times (exp) and token refresh mechanisms.
  2. Token Storage:

    • Ensure secure storage on the client side to prevent attacks like XSS (cross-site scripting) or CSRF (cross-site request forgery).
  3. Signature Verification:

    • Always verify the signature to ensure the token is not tampered with.

Differences Between Access Tokens, Refresh Tokens, and ID Tokens

When working with modern authentication systems, particularly with protocols like OAuth 2.0 and OpenID Connect (OIDC), three types of tokens are commonly used: Access Tokens, Refresh Tokens, and ID Tokens. Each serves a distinct purpose in the authentication and authorization processes.


1. Access Token

Purpose:

  • Grants a client application access to a protected resource (e.g., APIs, user data).
  • Used for authorization purposes.

Characteristics:

  • Issued by an Authorization Server (e.g., an Identity Provider like Azure AD, Okta, Ping Identity).
  • Contains information about:
    • Scopes/Permissions: Specifies what the client is allowed to do.
    • Expiration: Typically short-lived (minutes to hours) to minimize security risks.
  • Encoded as a JWT (JSON Web Token) or opaque token, depending on the implementation.
  • Sent by the client application in the Authorization header of API requests:

    Authorization: Bearer <access_token>

Example Use Case:

  • A mobile app retrieves an access token after user login and uses it to call a REST API for user data.

2. Refresh Token

Purpose:

  • Allows a client application to obtain a new access token without requiring the user to reauthenticate.
  • Extends the session duration without storing the user's credentials.

Characteristics:

  • Issued alongside the access token (in OAuth 2.0 flows where applicable).
  • Typically long-lived, but it can be revoked.
  • Sensitive and should never be exposed to the browser or client-side applications directly.
  • Used by the client application to call the Token Endpoint and request a new access token:

    POST /token Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=<refresh_token>

Example Use Case:

  • A user logs into a web application, and after the access token expires, the refresh token is used to silently fetch a new access token without requiring the user to log in again.

3. ID Token

Purpose:

  • Provides information about the authenticated user to the client application.
  • Used for authentication purposes.

Characteristics:

  • Issued by the Identity Provider (IdP) as part of the OpenID Connect (OIDC) flow.
  • Encoded as a JWT and contains claims about the user and the authentication event:
    • sub: User ID (subject)
    • name, email, preferred_username: User attributes
    • iat, exp: Issued-at and expiration timestamps
  • Typically consumed by the client application (e.g., a web app or mobile app) to establish a user session.

Example Use Case:

  • After a user logs in, the application uses the ID token to display the user's name and email in the UI and to verify the user's identity.

Key Differences

FeatureAccess TokenRefresh TokenID Token
PurposeAuthorize access to APIsObtain new access tokensAuthenticate the user
FormatJWT or opaque tokenOpaque token or JWTJWT
AudienceAPIs or resource serversAuthorization serverClient application
Expires Quickly?YesNoYes
Contains User Info?No (only scopes/permissions)NoYes (e.g., name, email, roles)
UsageSent with API requestsExchanged for new tokensValidates user identity
Security RisksExposure can lead to API misuseExposure can allow token theftExposure may reveal user info





When to Use Each Token

  1. Access Token:

    • Use it whenever the application needs to access protected resources or APIs.
    • Do not store sensitive user data in access tokens since their primary purpose is authorization.
  2. Refresh Token:

    • Use it when the access token expires, and you want to avoid asking the user to log in again.
    • Only store refresh tokens in secure server-side environments.
  3. ID Token:

    • Use it to validate the user's identity and establish a session in the client application after login.
    • Typically used in authentication scenarios (e.g., OpenID Connect).


Example Flow with All Three Tokens

Scenario: A Web Application Using OIDC and OAuth 2.0

  1. User Login:

    • The user logs into the web app using their credentials.
    • The Identity Provider authenticates the user and issues:
      • ID Token: For user authentication and establishing a session.
      • Access Token: For accessing APIs (e.g., fetching user profile data).
      • Refresh Token: For obtaining new access tokens when the current one expires.
  2. Accessing Protected Resources:

    • The web app uses the access token in the Authorization header to call APIs.
  3. Access Token Expiration:

    • When the access token expires, the refresh token is sent to the Identity Provider's Token Endpoint to request a new access token.
  4. Session Expiration:

    • If both the access token and refresh token expire, the user must log in again.

Access Management Fundamentals - Part 1

This post aims to compile a collection of 'knowledge bytes' on Access Management in the Identity and Access Management (IAM) space. While most of this information is generally available online, I thought it would be a good idea to put it all in one place for easy reference. 

What is SSO? 

SSO (Single Sign-On) is an authentication method that allows users to log in once and gain access to multiple applications or systems without needing to reauthenticate for each one. The SSO system handles the authentication process and enables seamless access across different services, improving user experience and reducing password fatigue. Key benefits of SSO include: Enhanced User Experience: Users only log in once, improving convenience. Increased Security: Centralized authentication can enforce stronger policies and reduce the risk of weak or reused passwords. Reduced Administrative Overhead: Fewer login issues and password resets. Improved Compliance: Centralized control over authentication improves auditability.

How to Enable SSO

Enabling SSO typically involves these steps:

1. Choose an Identity Provider (IdP)

  • An IdP authenticates users and provides authentication tokens to applications (Service Providers, SPs). Examples include:
    • Ping Identity
    • Okta
    • Azure AD
    • Google Workspace
    • AWS Cognito

2. Understand Supported Protocols

  • Ensure your IdP and SPs support the same authentication protocol, such as:
    • SAML (Security Assertion Markup Language): Common for enterprise applications.
    • OIDC (OpenID Connect): Built on OAuth 2.0, ideal for modern web and mobile apps.
    • Kerberos: Often used in on-premises environments.
    • OAuth 2.0: For authorization flows.

3. Configure the Identity Provider

  • Register the applications (SPs) in the IdP.
  • Configure metadata like:
    • Assertion Consumer Service (ACS) URL (for SAML).
    • Redirect URIs (for OIDC).
  • Set up authentication policies (e.g., MFA).

4. Configure the Service Providers

  • Integrate the SP with the IdP by providing:
    • IdP metadata (e.g., SAML metadata XML or OIDC discovery URL).
    • Public certificates for signature validation.
  • Map IdP user attributes (e.g., username, email, group membership) to the SP.

5. Set Up User Directory Integration

  • Connect the IdP to a user directory like:
    • On-premises Active Directory (AD).
    • Cloud-based directories like Azure AD or Okta.
    • Databases or APIs.

6. Test the Configuration

  • Perform user authentication flow testing:
    • Log in to the IdP and ensure access to SPs is seamless.
    • Validate attributes passed to SPs.

7. Implement Access Controls

  • Configure role-based access control (RBAC) or policies at the IdP and SP level.
  • Define which users/groups can access specific applications.

8. Roll Out to Users

  • Communicate the change to users.
  • Provide training on how to use SSO.
  • Ensure fallback mechanisms are in place in case of SSO issues.

9. Monitor and Maintain

  • Monitor authentication logs for security and compliance.
  • Regularly review access configurations and update as necessary.
  • Implement updates or patches for both the IdP and SPs.


What is SAML?

SAML (Security Assertion Markup Language) is an open standard for exchanging authentication and authorization data between parties, specifically between an Identity Provider (IdP) and a Service Provider (SP). It enables Single Sign-On (SSO) by allowing users to authenticate with the IdP and access multiple SPs without re-authenticating.

Key Components of SAML:

  1. Identity Provider (IdP):

    • Authenticates the user and provides an assertion (a token) to the SP, confirming the user's identity.
    • Examples: Ping Identity, Okta, Azure AD.
  2. Service Provider (SP):

    • Relies on the IdP to authenticate users.
    • Examples: Salesforce, Google Workspace, AWS.
  3. SAML Assertions:

    • XML-based messages that contain user authentication and attribute information.
    • Three types of assertions:
      • Authentication Assertions: Confirm the user's identity.
      • Attribute Assertions: Provide user details like name, email, or roles.
      • Authorization Assertions: Specify access permissions (optional).
  4. SAML Protocol:

    • Defines how messages are exchanged between the IdP and SP, typically over HTTP.

Why Use SAML for SSO?

1. Seamless User Experience

  • Users log in once with the IdP and gain access to multiple SPs without needing to re-enter credentials.

2. Enhanced Security

  • Reduces password proliferation by centralizing authentication with the IdP.
  • SAML tokens are signed and optionally encrypted, ensuring data integrity and confidentiality.

3. Standardized Interoperability

  • SAML is widely adopted across enterprise applications and supports cross-domain SSO.
  • Works well for web applications that need robust, secure authentication.

4. Centralized Identity Management

  • Makes it easier to enforce security policies, such as MFA, at a single point (the IdP).
  • Streamlines user provisioning and deprovisioning for connected SPs.

5. Compliance

  • Helps meet regulatory requirements by offering centralized audit trails for user authentication.

6. Flexibility

  • SAML supports both IdP-initiated SSO (user starts at the IdP) and SP-initiated SSO (user starts at the SP).

How SAML Enables SSO: A Simplified Workflow

  1. User Accesses the SP:

    • The user tries to access a protected resource on the SP.
  2. SP Redirects to IdP:

    • The SP redirects the user to the IdP for authentication with a SAML request.
  3. User Authenticates with IdP:

    • The user logs in at the IdP (or is already authenticated).
  4. IdP Sends a SAML Response:

    • The IdP generates a SAML assertion (signed XML) containing the user's identity and attributes.
    • The assertion is sent back to the SP via the user’s browser.
  5. SP Grants Access:

    • The SP validates the SAML assertion using the IdP’s public key.
    • If valid, the user is granted access to the requested resource.

When to Use SAML for SSO

  • Enterprise Applications: SAML is a great choice for traditional enterprise apps like Salesforce, SAP, or internal intranet portals.
  • Cross-Domain SSO: When users need to access applications hosted in different domains or organizations.
  • Legacy Systems: Many legacy applications support SAML but not modern protocols like OIDC.
  • High Security Requirements: SAML assertions can be signed and encrypted, providing a robust security mechanism.

Troubleshooting SAML SSO can be complex, but the right tools and approach make it manageable. Here's a breakdown of tools and techniques to identify and resolve issues:


1. Tools for Troubleshooting SAML SSO

a. Browser Extensions

  • SAML Tracer (Firefox/Chrome):
    • Captures SAML requests and responses as they flow between the browser, IdP, and SP.
    • Helps identify issues like missing or incorrect attributes, signature validation errors, or protocol mismatches.
  • SAML Chrome Panel (Chrome):
    • Adds a developer tools panel specifically for viewing SAML messages.
    • Simplifies the inspection of SAML assertions.

b. Logging Tools

  • IdP Logs:
    • Check logs in the Identity Provider (e.g., PingFederate, Okta, or Azure AD) for authentication errors or configuration mismatches.
  • SP Logs:
    • Examine the Service Provider logs to identify assertion validation issues or access control errors.

c. Debugging Proxies

  • Fiddler or Charles Proxy:
    • Captures HTTP traffic, including SAML requests/responses, for inspection.
  • Postman:
    • Useful for testing direct API interactions with SPs or IdPs.

d. Online Validators

e. Integrated Debugging Features

  • Many IdPs and SPs include built-in debugging tools:
    • PingFederate: SAML trace viewer in the admin console.
    • Okta: System logs with detailed SSO error codes.
    • Azure AD: Enterprise Application sign-in logs.

2. Common SAML SSO Issues and Troubleshooting Steps

a. Authentication Failures

  • Symptoms:
    • Users cannot log in or are redirected back to the login page.
  • Checks:
    • Verify the user exists in the IdP and is authorized to access the SP.
    • Ensure user credentials are correct and policies like MFA are satisfied.
    • Review IdP logs for authentication errors.

b. Signature Validation Errors

  • Symptoms:
    • SP rejects SAML assertions or metadata.
  • Checks:
    • Confirm the SP has the correct public key/certificate from the IdP.
    • Ensure the assertion or metadata is signed using the expected algorithm (e.g., SHA-256).

c. Attribute Mapping Issues

  • Symptoms:
    • Users authenticate successfully but lack permissions or roles in the SP.
  • Checks:
    • Confirm attribute mappings between IdP and SP are correct (e.g., email, roles, groups).
    • Use a SAML decoder to inspect the attributes in the SAML assertion.

d. Metadata Mismatches

  • Symptoms:
    • SP or IdP rejects the other party's metadata during setup or runtime.
  • Checks:
    • Ensure metadata (e.g., endpoints, entity IDs, certificates) matches between the IdP and SP.
    • Update metadata if either party changes configuration.

e. Clock Skew Issues

  • Symptoms:
    • Assertion expiration or "Request is too old" errors.
  • Checks:
    • Synchronize clocks between the IdP and SP (use NTP).
    • Verify NotBefore and NotOnOrAfter timestamps in the SAML assertion.

f. SSL/TLS Issues

  • Symptoms:
    • SSL errors or failures in HTTPS communication.
  • Checks:
    • Confirm certificates are valid and trusted by both IdP and SP.
    • Ensure secure ciphers are enabled.

g. Redirect Loop or Missing RelayState

  • Symptoms:
    • Users are redirected in a loop or end up on the wrong application page.
  • Checks:
    • Verify the RelayState parameter is included and correctly passed between the SP and IdP.
    • Ensure endpoints match the configuration.

h. SP-Initiated vs. IdP-Initiated Confusion

  • Symptoms:
    • Login works in one flow but not the other.
  • Checks:
    • Verify both flows are correctly configured in the IdP and SP.
    • Ensure the correct starting endpoint is used for each flow.


3. General Troubleshooting Tips

  1. Reproduce the Issue:

    • Identify the exact steps to replicate the problem.
    • Test in different browsers or with different users to rule out specific environment issues.
  2. Analyze Error Messages:

    • Use detailed logs from the IdP and SP to locate the root cause.
    • Decode and inspect SAML messages for structural or semantic issues.
  3. Test with Known Good Configurations:

    • Compare the failing setup with a working one, if available, to identify discrepancies.
  4. Enable Debugging:

    • Temporarily increase log verbosity on the IdP/SP to capture more details.
  5. Engage Vendor Support:

    • If you're using a commercial IdP or SP, their support team can often provide insights or confirm configuration details.

Wednesday, April 10, 2024

Java Function to Convert JSON Array to ArrayList


import org.json.JSONArray;
    
public static ArrayList toStringArray(JSONArray array) {
    if(array==null)
        return new ArrayList();
    
    ArrayList l = new ArrayList();
    for(int i=0; i<array.length(); i++) {
        l.add( array.optString(i) );
    }
    return l;
}

Tuesday, December 7, 2021

PingFederate - Enable Radius Logging

To enable logging for the Radius component of PingFederate do the following:

Add the following lines to the log4j2.xml file to line 1267 (just before the "Loggers" block):

<Logger name="com.pingidentity.plugins.pcvs.pingid" level="DEBUG" />

<Logger name="org.tinyradius" level="DEBUG" />

<Logger name="org.newtinyradius" level="DEBUG" />

and change the below:

<AsyncRoot level="INFO" includeLocation="false">

to the below

<AsyncRoot level="DEBUG" includeLocation="false">

Friday, July 17, 2020

Creating your own CA and development certificates

Into the world of SSL/TLS we go again.

For this I will need my own CA. I seem to always set one up but forget the process I followed so I’m documenting it here now. Hopefully you can benefit from this.

Step 1 - Create your private key
First we need to create our private key with the following command:

  • openssl genrsa -aes256 -out myCA.key 2048

The above step will produce a file called myCA.key which is your private key for the CA. Make sure you don’t lose this or the passphrase you provided to generate the key. Normally I suspect this key would be kept in an HSM device.

Step 2 - Create your certificate
Now we will generate the root certificate using this command using the key file we just created, you will need to enter the passphrase you used in step 1.
  • openssl req -x509 -new -nodes -key myCA.key -sha256 -days 1825 -out myCA.pem

Make sure you are careful when you input the values as the input does not support backspace, so if you stuff up something rather run this step again.

You can check out the cert details by running the following command:
  • openssl x509 -in myCA.pem -text -noout

This should show like below, make sure you double check the subject of the certificate that it matches the values you inputted:


Now you have completed this step you can import the certificate into your trusted root store so that that any certificates you sign/create will be trusted.

Get the certificate on you PC and install. On windows it’s easiest to rename the certificate .pem file to .cert like so:

Right click on and chose install 'Install Certificate'
Make sure you add it to your trusted root certificates:

Step 3 - Creating you development server certificates
Now that we have a CA we can sign certificates :-) :-)

Follow this process to create and sign a certificate.

  1. Create a private key with the following command:
  • openssl genrsa -out dev.myserver.key 2048
  1. Ok this is where it gets tricky! You use the following command to create a certificate signing request (CSR) using the private key you created in the previous step:
  • openssl req -new -key dev.myserver.key -out dev.myserver.csr
BUT WAIT! you more than likely want to have multiple names of the dev server that you would reference by DNS/IP e.g. I have the following for my dev server:
  • mypingaccess
  • mypingaccess.mylab.co.za
  • mypingaccess.sandbox.co.za

In order to add these alternative names, also known as subject alternative names (SANs) you need to do the following:

b1) create a configuration file that contains the the names like so:

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name
req_extensions     = req_ext
prompt = no
[ req_distinguished_name ]
countryName                = ZA
stateOrProvinceName        = Gauteng
localityName               = Sandton
organizationName           = IT
commonName                 = mypingaccess.mylab.co.za
[ req_ext ]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment
[ alt_names ]
DNS.1   = mypingaccess
DNS.2   = mypingaccess.mylab.com
DNS.3   = mypingaccess.sandbox.co.za

I created the file in /tmp/san.cnf

b2) then I ran the following command to create the CSR: 
  • openssl req -out dev.myserver.csr -newkey rsa:2048 -nodes -keyout dev.myserver.private.key -config /tmp/san.cnf
b3) now make sure the SANs are part of the CSR with the following command:
  • openssl req -noout -text -in dev.myserver.csr | grep DNS
You should see the following output:
DNS:mypingaccess, DNS:mypingaccess.mylab.com, DNS:mypingaccess.sandbox.co.za

  1. Now that we have a CSR we can sign it with our CA. Use the following command to sign it:
  • openssl x509 -req -in dev.myserver.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out dev.myserver.crt -days 825 -sha256 -extfile /tmp/san.cnf -extensions req_ext
  1. Now check that the SANs are in the newly signed cert with the following command:
  • openssl x509 -in dev.myserver.crt -text -noout | grep DNS
You should have the following:
DNS:mypingaccess, DNS:mypingaccess.mylab.com, DNS:mypingaccess.sandbox.co.za

This part really broke a number of times by following a number of online posts because they did not include the -extfile and -extensions option, also when they did include it they reference another cnf file and not the one I used to create the CSR. What I also found was that if this file had the incorrect parameters in it that the DNS SANs name would be stripped (not included) in the certificate (the CRT file).

Cool now that we have the certificate we need to install it on our server. This will depend on the application/web server you are using, but please keep in mind that because the server is encrypting the content it will need both the private and public keys.

Some application servers like Apache will allow you to provide both the private and public keys separately, while other servers will require a keypair. Here are the steps to create a PKCS#12 keypair:

  • openssl pkcs12 -export -out certificate.pfx -inkey dev.myserver.private.key -in dev.myserver.crt

This will prompt you for a password to protect the keypair because it contains the private key which you should not share with a public entity. You should now have a certificate.pfx file you can import as required.

Here’s an example of how I imported this in PingAccess:

1) Go to the Security -> Key Pairs option and click 'Import'


2) Provide an alias for the key pair, provide the password and upload the 'certificate.pfx'

3) Click ‘Save’ on the bottom right. You should get the following pop up->


4) View the key pair:




If you connect to PingAccess you will still get a certificate error as you now need to configure the HTTP listener to use the key pair.


You will see that the current ADMIN key pair was created on installation and is set as the ADMIN HTTPS listener:



Now go back to the key pair list and click the pencil icon on the key pair you imported. From the drop down select the 'Assign HTTPS Listener' option:

Choose where you want to use the key pair:


The good news is no restart required. Open another browser instance or incognito mode and you connection should be secure:


You can view the certificate and confirm the path:


Followers