Public note
Understanding OAuth2 vs OpenID Connect Through Solace Message VPN Roles
The article distinguishes between OAuth2 and OpenID Connect (OIDC) in Solace Message VPN roles, explaining 'client' mode as identity-focused authentication and 'resource-server' mode as permission-based authorization.
Introduction
One of the most confusing parts of OAuth2 and OpenID Connect (OIDC) is understanding the difference between:
- Authentication
- Authorization
- OAuth Client
- Resource Server
- ID Token
- Access Token
This confusion becomes even more noticeable when reading the Solace Message VPN OAuth documentation because Solace supports two OAuth roles:
clientresource-server
At first glance, both modes appear identical because:
External client -> sends token -> Solace
However, the semantic meaning of the token and the validation behavior are very different.
This article explains those differences in detail.
Core Concept
The biggest misunderstanding usually comes from assuming:
"If a server validates a token, it must always be a Resource Server."
That is not always true.
The real distinction is:
| Mode | Main Concern |
|---|---|
client | Who is the user? (Authentication) |
resource-server | What is this token allowed to do? (Authorization) |
OAuth2 vs OpenID Connect
Before understanding Solace roles, we must separate OAuth2 from OpenID Connect.
OAuth2
OAuth2 is fundamentally an authorization protocol.
Its primary concern is:
What is this application allowed to access?
The main artifact is:
Access Token
Example:
"This application may publish messages."
Typical access-token claims:
{
"scope": "publish subscribe",
"aud": "solace"
}
OAuth2 focuses on permissions.
OpenID Connect (OIDC)
OIDC is an authentication layer built on top of OAuth2.
Its primary concern is:
Who is this user?
The main artifact is:
ID Token
Example:
"This user is enum@company.com"
Typical ID-token claims:
{
"sub": "user-123",
"email": "enum@company.com",
"name": "Enum"
}
OIDC focuses on identity.
Why the Solace Documentation Feels Confusing
The Solace documentation section is titled:
Client Authentication
not:
Authorization
This feels strange because OAuth2 is normally associated with authorization.
The reason is:
Solace treats broker connection acceptance as an authentication problem.
In other words:
"Should this client be allowed to CONNECT to the broker?"
For a messaging broker, token validation happens during connection establishment.
Therefore:
- identity checks
- token validation
- permission validation
all happen during the CONNECT phase.
Solace OAuth Roles
Solace Message VPN supports:
oauth-role clientoauth-role resource-server
Both modes allow external clients to bring tokens.
However, the meaning of the token differs.
Resource Server Mode
Main Idea
In resource-server mode:
The token represents authorization.
The main question is:
"Is this token allowed to access this broker?"
Typical Flow
Rendering Mermaid diagram from the saved markdown source.
sequenceDiagram
participant App
participant Solace
participant IdP
App->>Solace: Access Token
Solace->>IdP: Introspection Request
IdP-->>Solace: active=true
Solace-->>App: Connection AcceptedImportant Characteristics
| Feature | Behavior |
|---|---|
| Token Type | Access Token |
| Main Focus | Authorization |
| Validation | Introspection or JWT validation |
| Scope Validation | Important |
| Revocation Awareness | Important |
Typical Claims
{
"scope": "publish subscribe",
"aud": "solace",
"exp": 1730000000
}
Client Mode
Main Idea
In client mode:
The token represents identity.
The main question is:
"Who is this user?"
This mode behaves more like OpenID Connect authentication.
Typical Flow
Rendering Mermaid diagram from the saved markdown source.
sequenceDiagram
participant Client
participant Solace
participant JWKS
Client->>Solace: ID Token (JWT)
Solace->>JWKS: Fetch Public Keys
JWKS-->>Solace: Public Key
Solace-->>Client: Connection AcceptedImportant Characteristics
| Feature | Behavior |
|---|---|
| Token Type | ID Token |
| Main Focus | Authentication |
| Validation | JWT Signature Verification |
| Identity Claims | Important |
| Scope Validation | Less Important |
JWT Structure
A JWT consists of three parts:
header.payload.signature
Example:
aaaaa.bbbbb.ccccc
Where:
| Part | Meaning |
|---|---|
aaaaa | Base64URL-encoded header |
bbbbb | Base64URL-encoded payload |
ccccc | Base64URL-encoded signature |
JWT Signature Verification
This is one of the most important concepts in OIDC.
Step 1: Extract Header and Payload
The verifier reconstructs:
signingInput =
base64url(header)
+ "."
+ base64url(payload)
Example:
aaaaa.bbbbb
Step 2: Extract Signature
The third JWT part:
ccccc
is the signature.
Step 3: Find the Correct Public Key
The JWT header contains:
{
"alg": "RS256",
"kid": "abc123"
}
The verifier fetches the JWKS document:
{
"keys": [
{
"kid": "abc123",
"kty": "RSA"
}
]
}
The verifier selects the key with:
kid == "abc123"
Step 4: Verify Signature
The Identity Provider originally created:
signature = Sign(privateKey, signingInput)
The verifier performs:
Verify(publicKey, signingInput, signature)
Meaning:
"Was this signature really created using the corresponding private key?"
Important Clarification
The public key does NOT decrypt the JWT payload.
JWT payloads are usually only Base64URL-encoded.
Anyone can decode them.
The public key is only used to verify:
Integrity + authenticity
Specifically:
- The token was issued by the trusted Identity Provider
- The payload was not modified
What Happens If Payload Changes?
Suppose the original payload was:
{
"scope": "read"
}
An attacker changes it to:
{
"scope": "admin"
}
Now:
header.payload
changes.
The existing signature no longer matches.
Verification fails.
Why kid Exists
Identity Providers rotate keys.
Therefore, multiple public keys may exist simultaneously.
The JWT header includes:
{
"kid": "abc123"
}
so the verifier knows which key to use.
Authentication vs Authorization in Practice
This is the easiest way to think about it.
Client Mode
Equivalent to:
"Who are you?"
Like showing an employee badge.
Resource Server Mode
Equivalent to:
"What are you allowed to do?"
Like checking room-access permissions.
Why Both Can Still Use JWT
Modern JWTs often contain both:
- identity information
- authorization information
Example:
{
"sub": "enum",
"email": "enum@company.com",
"scope": "publish"
}
Therefore the same JWT technology can support:
- OIDC authentication
- OAuth2 authorization
The difference is semantic interpretation.
Final Mental Model
oauth-role client
Solace asks:
"Who is this client?"
Focus:
- identity
- authenticated user
- OIDC semantics
oauth-role resource-server
Solace asks:
"What is this token allowed to do?"
Focus:
- scopes
- permissions
- authorization
- token status
Final Summary Table
| Feature | Client Mode | Resource Server Mode |
|---|---|---|
| Protocol Semantics | OpenID Connect | OAuth2 |
| Main Purpose | Authentication | Authorization |
| Main Token | ID Token | Access Token |
| Main Concern | Identity | Permissions |
| Typical Validation | JWT Signature Validation | Introspection/JWT Validation |
| Uses JWKS | Yes | Often |
| Uses Introspection | Rarely | Frequently |
Checks scope | Sometimes | Common |
Checks sub/email | Important | Less Important |
Conclusion
The key insight is:
Both modes may receive tokens from external clients.
However:
clientmode interprets the token primarily as an identity assertionresource-servermode interprets the token primarily as an authorization artifact
This distinction explains why the Solace documentation discusses OAuth inside a "Client Authentication" section even though OAuth2 is fundamentally an authorization framework.