Simple Auth Protocol
1. Purpose and Scope
This document defines a simplified authentication protocol inspired by OpenID 1.0.
Design constraints:
- One fixed provider hostname (CME).
- No OpenID discovery.
- No Diffie-Hellman association exchange.
- CME and Relying Parties (RPs) share one secret delivered out-of-band (for example on paper).
- The protocol does not address user roles and permissions; it only handles authentication.
This is intentionally not full OpenID interoperability. It is a constrained profile for closed deployments.
2. Roles
- CME (Provider): central login server with fixed hostname.
- RP (Relying Party): application server that delegates user authentication to CME.
- User Agent: browser.
Simple interaction overview:
User Agent -> RP : requests protected page
RP -> User Agent : 302 redirect to CME provider
User Agent -> CME : authenticates user
CME -> User Agent : 302 redirect back to RP callback
User Agent -> RP : sends signed id_res response
RP -> User Agent : creates local session
3. Static Configuration
Each RP and CME must be configured with:
provider_endpoint: fixed CME URL, for examplehttps://knihy.90.cz/login.php.shared_secret: high-entropy key exchanged offline (minimum 32 random bytes, base64 encoded in config).allowed_return_to: exact RP callback URL(s), for examplehttp://127.0.0.1:5004/auth/callbackandhttps://shift-planner.mathbox.90.cz/auth/callbackclock_skew_seconds: default120.nonce_ttl_seconds: default600.
If the same CME provider is used for both local and production RP instances, both callback URLs must be allowlisted on the provider side.
4. Protocol Messages
All messages are sent indirectly through browser redirects using HTTP 302 responses.
4.1 Authentication Request (RP → CME)
RP redirects browser to provider_endpoint with query parameters:
mode=checkid_setupreturn_to=<exact RP callback URL>rp_nonce=<RP-generated random nonce>op_ts=<unix timestamp seconds generated by RP>sig=<Base64(HMAC-SHA256(shared_secret, signing_input))>
Signing input is built in this exact order: mode, return_to, op_ts and rp_nonce. CME selects identity after successful user authentication. Nonce value should be a random UUID, preferably UUIDv4.
Accepted nonces must be stored on the CME side for the duration defined by nonce_ttl_seconds.
4.2 Positive Authentication Response (CME → RP)
After user login and consent, CME redirects browser to return_to with:
mode=id_resuseremail=<user email>(optional)username=<user name>(optional)userid=<user id>return_to=<same value as request>rp_nonce=<echo from request>op_ts=<unix timestamp seconds generated by CME>sig=<Base64(HMAC-SHA256(shared_secret, signing_input))>
Signing input is built in this exact order: mode, useremail, username, userid, return_to, rp_nonce, op_ts. If useremail or username is omitted, RP and CME must treat that field as an empty string when rebuilding the signed payload.
4.3 Negative Response (CME -> RP)
It's not necessary to implement it
If authentication is denied or canceled:
mode=cancel
No signature is required for cancel responses.
4.4 Diagram
Browser -> RP : GET protected page
RP -> Browser : 302 provider_endpoint?mode=checkid_setup...
Browser -> CME : GET /login.php?... + user login
CME -> Browser : 302 return_to?mode=id_res&...&sig=...
Browser -> RP : GET /auth/callback?...&sig=...
RP -> Browser : creates session and redirects to original page
5. Validation Rules
5.1 Authentication Request Validation (at CME)
CME accepts an authentication request only if all checks pass:
modeequalscheckid_setup.- All required fields are present:
mode,return_to,rp_nonce,op_ts,sig. return_toexactly matches an allowlisted callback URL for the requesting RP.rp_nonceformat is valid (recommended: UUIDv4).op_tsis within allowed clock skew.- Recomputed HMAC-SHA256 signature over
mode, return_to, op_ts, rp_nonceexactly matchessig(constant-time compare).
If any check fails, reject the request and log the reason.
5.2 Positive Authentication Response Validation (at RP)
Before accepting identity from id_res, RP must validate:
modeequalsid_res.- All required fields are present:
mode,userid,return_to,rp_nonce,op_ts,sig.useremailandusernameare optional and may be empty. return_toexactly matches the callback URL that is currently handling the response and an allowlisted RP callback URL.rp_nonceexists, matches the nonce stored for this login attempt, and has not been used before.op_tsis within allowed clock skew.- Recomputed HMAC-SHA256 signature over
mode, useremail, username, userid, return_to, rp_nonce, op_tsexactly matchessig(constant-time compare). - User mapping/authorization policy is satisfied before creating RP session.
If any check fails, reject authentication, clear one-time state as needed, and log the reason.
5.3 Cancel Response Handling
When RP receives mode=cancel, authentication must be treated as failed or canceled.
- Do not create RP session.
- Invalidate pending login state (nonce/session binding) for that attempt.
- Show a user-facing canceled/failed login message.
No signature verification is required for mode=cancel.
6. Security Requirements
- Use HTTPS for all CME and RP endpoints.
- Shared secret must be random and never sent in protocol messages.
- Rotate
shared_secretperiodically (for example every 90 days). - Keep previous secret during grace period to avoid hard cutover failures.
- Store used nonces for at least
nonce_ttl_secondsto prevent replay. - Limit and monitor failed signature checks.
- Use constant-time comparison for signatures.
- Keep server clocks synchronized (NTP).
6.1 Message Signing
This profile uses deterministic signing with a shared secret.
- The message includes
sig:Base64(HMAC-SHA256(shared_secret, token_contents)). token_contentsis built by serializing key-value lines in protocol-defined order:- Request (
mode=checkid_setup):mode,return_to,op_ts,rp_nonce - Positive response (
mode=id_res):mode,useremail,username,userid,return_to,rp_nonce,op_ts shared_secretMUST NOT be sent in request/response parameters and MUST NOT appear intoken_contents.- Key-value line format:
field_name:field_value\n
Rules for token_contents serialization are aligned with OpenID key-value form:
- No spaces before or after
:. - Use Unix newline (
\n, ASCII 10). - Include newline after every line.
- Use UTF-8 encoding.
- Optional response fields
useremailandusernamemust still occupy their signed position; when absent, serialize them as empty values.
Example token contents for positive response:
mode:id_res
useremail:<value or empty>
username:<value or empty>
userid:<value>
return_to:<value>
rp_nonce:<value>
op_ts:<value>
RP verification MUST rebuild token_contents from received fields in protocol-defined order and compare HMAC in constant time.
6.2 Security Risks and Mitigations
- Man-in-the-middle (MITM): An attacker intercepts traffic between browser, RP, and CME.
Mitigation: Enforce HTTPS/TLS everywhere, enable HSTS, and reject mixed-content deployments. - Message tampering: An attacker modifies
id_resfields in transit.
Mitigation: Verifysigover the fixed field order and use constant-time signature comparison. - Replay attacks: A previously valid signed response is reused later.
Mitigation: Validateop_tsfreshness and store used nonces fornonce_ttl_seconds. - Return URL abuse / open redirect: Attacker tries to force callbacks to an untrusted URL.
Mitigation: CME must strictly allowlistreturn_to; RP must exact-match callback URL on receipt. - Shared secret leakage: Secret is exposed via logs, config mistakes, or backups.
Mitigation: Store in a secret manager, never log secret material, restrict access, and rotate secrets regularly. - Weak nonce generation: Predictable
rp_nonceenables replay or request correlation attacks.
Mitigation: Generate nonces with a cryptographically secure RNG and sufficient entropy. - Clock manipulation / skew issues: Incorrect system time weakens
op_tsfreshness checks.
Mitigation: Use NTP, enforce bounded clock skew, and alert on significant clock drift. - Login CSRF / session confusion at RP: Browser is redirected with a valid response bound to the wrong local session.
Mitigation: Bindrp_nonceto RP session state and invalidate it immediately after use. - Provider impersonation: Client is redirected to a fake provider endpoint.
Mitigation: Pin the fixed CME hostname in configuration and require valid TLS certificate checks.
7. Minimal Example
7.1 RP → CME
GET https://knihy.90.cz/login.php?
mode=checkid_setup&
return_to=https%3A%2F%2Fshift-planner.mathbox.90.cz%2Fauth%2Fcallback&
op_ts=1772518394&
rp_nonce=6f7b6b5f9a2c4d5f&
sig=iKqz7ejTrflNJquQ07r9SiCDBww7zOnAFO4EpEOEfAs=
7.2 CME → RP
GET https://shift-planner.mathbox.90.cz/auth/callback?
mode=id_res&
useremail=jan.jirout%40internet-handel.cz&
username=Honza&
userid=24234&
return_to=https%3A%2F%2Fshift-planner.mathbox.90.cz%2Fauth%2Fcallback&
rp_nonce=6f7b6b5f9a2c4d5f&
op_ts=1772525600&
sig=iKqz7ejTrflNJquQ07r9SiCDBww7zOnAFO4EpEOEfAs=
7.3 CME Negative Response (Cancel)
GET <RP app url>/auth/callback?
mode=cancel
No signature is required for cancel responses.
8. Implementation Checklist
8.1 Provider
- Fixed endpoint and RP allowlists configured.
- Shared secret loaded securely.
- Request validation implemented.
- Assertion signing implemented with deterministic field order.
- Audit logging (request id, RP id, decision, reason).
8.2 Relying Party
- Nonce/state storage implemented.
- Callback exact URL validation implemented.
- Signature verification implemented.
- Replay and timestamp checks implemented.
- Local user session issuance only after full validation.