AuthX — Security Architecture
AuthX is the identity authority for DjangoPlay authentication.
On this page ▾
The security model is based on:
- Password hashing
- RSA-signed JWTs
- Issuer and audience validation
- Stateful refresh-token control
- Backend service authentication
- Restricted CORS origins
- Strict separation of AuthX and DjangoPlay credentials
- Protection of private signing keys
1. Security Architecture Overview
flowchart TD
CLIENT["Browser / API Client"]
AUTH["AuthX<br/><b>Identity Authority</b>"]
PASSWORD["Password Hashing<br/>Credential Protection"]
JWT["JWT Issuance<br/><b>RS256</b>"]
REFRESH["Refresh Token State<br/>Rotation / Revocation"]
SERVICE["Internal Service API<br/><b>X-Service-Token</b>"]
DB["AuthX PostgreSQL"]
PRIVATE["RSA Private Key<br/><b>JWT_PRIVATE_KEY</b>"]
PUBLIC["RSA Public Key / JWKS<br/><b>JWT_PUBLIC_KEY</b>"]
POLICY["Issuer + Audience Validation<br/>JWT_ISSUER / JWT_AUDIENCE"]
CORS["CORS Origin Controls<br/><b>CORS_ORIGINS</b>"]
CLIENT --> AUTH
AUTH --> PASSWORD
AUTH --> JWT
AUTH --> REFRESH
AUTH --> CORS
JWT --> PRIVATE
PRIVATE --> JWT
JWT --> PUBLIC
PUBLIC --> POLICY
REFRESH --> DB
PASSWORD --> DB
SERVICE --> AUTH
classDef client fill:#f5f3ed,stroke:#777,stroke-width:1px,color:#444
classDef auth fill:#e5f6f1,stroke:#159a7a,stroke-width:2px,color:#075d4b
classDef security fill:#fceee8,stroke:#d65a2a,stroke-width:2px,color:#7d321c
classDef crypto fill:#eeeeff,stroke:#5a55c9,stroke-width:2px,color:#403b91
classDef data fill:#e6f1fb,stroke:#2474b5,stroke-width:2px,color:#174d7a
class CLIENT client
class AUTH auth
class PASSWORD,REFRESH,SERVICE,CORS,POLICY security
class JWT,PRIVATE,PUBLIC crypto
class DB data
`The important security boundary is that AuthX owns identity credentials and JWT signing, while consuming applications only receive what they need to authenticate and verify users.
2. Password Security
AuthX does not store plaintext passwords.
The identity record stores a password hash rather than the original password.
User Password
│
▼
Password Hashing
│
▼
password_hash
│
▼
AuthX PostgreSQLThe password credential therefore remains inside AuthX.
DjangoPlay does not need to reproduce AuthX's password storage or hashing logic.
AuthX is responsible for:
- Receiving passwords during authentication workflows
- Hashing passwords
- Storing password hashes
- Verifying supplied passwords against stored hashes
The application database should never contain plaintext AuthX passwords.
3. JWT Signing Architecture
AuthX is the sole JWT signing authority for AuthX identity tokens.
signs
AuthX ─────────────────────────► JWT
│ │
│ JWT_PRIVATE_KEY │
│ │
│ ▼
│ DjangoPlay
│ │
│ │ verifies
│ ▼
└──────────────────────── AuthX Public KeyAuthX uses an RSA key pair and the configured signing algorithm:
JWT_ALGORITHM=RS256The private key is configured through:
JWT_PRIVATE_KEYThe corresponding public key is configured through:
JWT_PUBLIC_KEYThe private key belongs exclusively to AuthX.
DjangoPlay and other consumers must never receive the AuthX private signing key.
4. RSA Key Protection
AuthX's RSA private key is one of the most sensitive credentials in the system.
JWT_PRIVATE_KEY
│
▼
AuthX
│
│ signs
▼
JWTThe public key can safely be distributed to consumers for signature verification.
The private key must:
- Remain server-side
- Never be exposed to browser clients
- Never be committed to Git
- Never be included in frontend assets
- Never be shared with consuming applications
- Be replaced if it is suspected of being compromised
A fresh RSA key pair should be generated for a new deployment.
Example:
openssl genrsa -out jwt_private.pem 2048
openssl rsa \
-in jwt_private.pem \
-pubout \
-out jwt_public.pemValidate the private key:
openssl rsa -in jwt_private.pem -check -nooutExpected:
RSA key okVerify that the public key corresponds to the private key:
openssl rsa \
-in jwt_private.pem \
-pubout \
-outform PEM | diff - jwt_public.pemNo output and exit status 0 indicate that the public and private keys match.
When storing PEM values in environment configuration, preserve PEM line
boundaries using literal \n sequences.
Do not introduce arbitrary backslashes into the Base64 key material.
5. JWT Claims
AuthX-issued identity JWTs contain standard identity and token metadata.
The important security claims include:
sub
iss
aud
iat
exp
jti
typeIdentity access tokens additionally carry identity attributes such as:
email
username
is_verified
is_staff
is_superuserThe token type distinguishes access and refresh tokens:
type=access
type=refreshThe jti claim provides a unique JWT identifier and is particularly important
for token tracking and refresh-token state.
6. Issuer Validation
AuthX identifies itself as the JWT issuer using:
JWT_ISSUERThe current DjangoPlay deployment uses:
JWT_ISSUER=https://auth.djangoplay.orgThe issuer identifies the identity authority asserted by the token.
It is not necessarily the network address used to reach the AuthX process.
For example:
AuthX runtime:
APP_BASE_URL=http://127.0.0.1:8100
JWT identity authority:
JWT_ISSUER=https://auth.djangoplay.orgThese are intentionally different concepts.
Consumers should validate the iss claim against their configured AuthX issuer.
This prevents a token issued by an unexpected authority from being silently accepted.
7. Audience Validation
AuthX also uses:
JWT_AUDIENCEThe current DjangoPlay configuration uses:
JWT_AUDIENCE=djangoplayThe audience identifies the application for which the JWT is intended.
The consumer should verify the aud claim as well as the signature and issuer.
Conceptually:
JWT
│
├── Signature ──────► valid AuthX signature?
│
├── iss ────────────► expected AuthX issuer?
│
└── aud ────────────► expected application?A valid RSA signature alone is therefore not sufficient.
8. Access Tokens and Refresh Tokens
AuthX separates short-lived access tokens from longer-lived refresh tokens.
The configured defaults are:
Access token:
60 minutes
Refresh token:
30 daysThese values are controlled by:
JWT_ACCESS_TOKEN_EXPIRE_MINUTES
JWT_REFRESH_TOKEN_EXPIRE_DAYSAccess tokens are intended for normal authenticated API access.
Refresh tokens are used to obtain new access tokens and therefore require additional server-side protection.
9. Refresh Token State
Refresh tokens are stateful.
AuthX maintains refresh-token records in PostgreSQL so that refresh tokens can be controlled after issuance.
Conceptually:
Refresh JWT
│
│ jti
▼
AuthX
│
▼
Refresh Token Record
│
├── active
├── revoked
└── expiredThis allows AuthX to support server-side revocation and refresh-token lifecycle management.
Access tokens, by contrast, are designed to be validated cryptographically without requiring a database lookup for every request.
10. Internal Service Authentication
AuthX exposes internal identity-management operations for trusted backend consumers.
DjangoPlay authenticates these requests using:
X-Service-TokenThe configured credential is:
AUTHX_SERVICE_TOKENThe security boundary is:
DjangoPlay
│
│ X-Service-Token
▼
AuthX /internal/*The service token is a backend credential.
It must never be:
- Embedded in frontend JavaScript
- Sent by browser clients
- Stored in browser storage
- Included in public documentation with its real value
- Committed to source control
The same service credential must be configured consistently on the DjangoPlay and AuthX sides.
11. Public vs Internal API Boundary
AuthX separates public authentication functionality from internal identity-management functionality.
AuthX
│
┌───────────┴───────────┐
│ │
▼ ▼
Public Authentication Internal APIs
│ │
│ │ X-Service-Token
▼ ▼
Browser / Clients DjangoPlayPublic clients should not receive the credentials required to access internal identity-management endpoints.
The internal service token establishes a backend-to-backend trust boundary.
12. CORS Protection
AuthX supports explicit browser-origin configuration through:
CORS_ORIGINSDevelopment may contain local DjangoPlay origins such as:
https://app.lvh.me:9999
http://app.lvh.me:3333Production should contain only the browser origins that actually need to communicate with AuthX.
Example production configuration:
CORS_ORIGINS=https://app.djangoplay.org,https://issues.djangoplay.orgCORS configuration should not be treated as authentication.
It controls which browser origins may make cross-origin requests; it does not replace JWT validation or backend service authentication.
Do not unnecessarily add:
*or unrelated production origins.
13. Credential Separation
DjangoPlay and AuthX deliberately maintain separate credentials.
DjangoPlay
│
├── DB_PASSWORD
├── DATABASE_URL
└── JWT_SIGNING_KEYversus:
AuthX
│
├── POSTGRES_PASSWORD
├── DATABASE_URL
├── DATABASE_URL_SYNC
├── JWT_PRIVATE_KEY
├── JWT_PUBLIC_KEY
├── JWT_ISSUER
├── JWT_AUDIENCE
└── AUTHX_SERVICE_TOKENThese credentials must not be confused.
In particular:
DB_PASSWORD != POSTGRES_PASSWORD
JWT_SIGNING_KEY != JWT_PRIVATE_KEY
APP_BASE_URL != JWT_ISSUER
AUTHX_SERVICE_TOKEN != database passwordThe current configuration explicitly uses POSTGRES_PASSWORD for the AuthX
PostgreSQL role; there is no AUTHX_DB_PASSWORD configuration key.
14. AuthX Configuration Boundary
For a DjangoPlay deployment, AuthX configuration is maintained separately from the main DjangoPlay secrets.
~/.dplay/
│
├── .secrets
│ │
│ └── DjangoPlay secrets
│
└── .authx
│
└── AuthX configurationThe AuthX configuration contains its own:
- Database credentials
- JWT signing keys
- JWT configuration
- Service authentication credential
- CORS configuration
This separation reduces accidental credential mixing between the two systems.
15. Database Security
AuthX stores identity data in its own PostgreSQL database.
The database contains sensitive identity information, including password hashes and refresh-token state.
The AuthX PostgreSQL credentials are:
POSTGRES_USER
POSTGRES_PASSWORD
POSTGRES_DBand the corresponding connection strings:
DATABASE_URL
DATABASE_URL_SYNCThe AuthX database credentials are separate from DjangoPlay's database credentials.
Database access should therefore remain restricted to the AuthX runtime and trusted administrative operations.
16. Security Boundary Summary
flowchart LR
CLIENT["Public Client"]
AUTHX["AuthX"]
INTERNAL["Trusted Backend<br/>DjangoPlay"]
DB["AuthX PostgreSQL"]
KEYS["RSA Key Material"]
CLIENT -->|"Authentication"| AUTHX
INTERNAL -->|"X-Service-Token"| AUTHX
AUTHX -->|"Read / write identity data"| DB
AUTHX -->|"JWT_PRIVATE_KEY<br/>signs tokens"| KEYS
KEYS -->|"JWT_PUBLIC_KEY / JWKS<br/>verification"| CLIENT
KEYS -->|"Public-key verification"| INTERNAL
classDef client fill:#f5f3ed,stroke:#777,stroke-width:1px,color:#444
classDef auth fill:#e5f6f1,stroke:#159a7a,stroke-width:2px,color:#075d4b
classDef trusted fill:#eeeeff,stroke:#5a55c9,stroke-width:2px,color:#403b91
classDef data fill:#e6f1fb,stroke:#2474b5,stroke-width:2px,color:#174d7a
class CLIENT client
class AUTHX auth
class INTERNAL,KEYS trusted
class DB dataThe major trust boundaries are:
| Boundary | Protection |
|---|---|
| Client → AuthX | Authentication protocol and token issuance |
| Backend → AuthX | X-Service-Token |
| AuthX → PostgreSQL | Restricted database credentials |
| AuthX → JWT | RSA private-key signing |
| Consumer → JWT | Public-key signature verification |
| JWT → Consumer | Issuer and audience validation |
| Browser → AuthX | Explicit CORS origins |
| AuthX configuration | Separate .authx deployment configuration |
17. Security Rules
The following rules should be treated as mandatory:
- Never store plaintext passwords.
- Never expose
JWT_PRIVATE_KEY. - Never expose
AUTHX_SERVICE_TOKENto browser clients. - Never commit private keys or service credentials to Git.
- Use
RS256for the current AuthX JWT architecture. - Validate JWT signature, issuer, audience, expiration, and token type as appropriate for the operation.
- Keep AuthX database credentials separate from DjangoPlay credentials.
- Use explicit production CORS origins.
- Generate a fresh RSA key pair for a new deployment.
- Rotate compromised private keys and service tokens.
- Keep AuthX identity persistence inside AuthX rather than duplicating credential-management logic in DjangoPlay.
- Treat AuthX as the identity and JWT-signing authority.
18. Security Responsibilities
The security responsibilities are deliberately divided between AuthX and its consumers.
AuthX
AuthX is responsible for:
- Password credential protection
- Identity persistence
- Password verification
- JWT signing
- Refresh-token state
- JWT issuer and audience configuration
- Internal service authentication
- CORS configuration
- Protection of private signing keys
DjangoPlay
DjangoPlay is responsible for:
- Securely storing its AuthX service credential
- Verifying AuthX-issued JWTs
- Configuring the expected issuer and audience
- Protecting local application sessions and credentials
- Enforcing application-level authorization
- Never exposing AuthX private credentials
This separation ensures that authentication authority and application-level authorization remain distinct concerns.
19. Security Model Summary
AuthX's security model can be summarized as:
AUTHX
│
┌─────────┼─────────┐
│ │ │
▼ ▼ ▼
Credentials JWTs Internal API
│ │ │
▼ ▼ ▼
Hashing RS256 Service Token
│
┌───────┴───────┐
│ │
▼ ▼
Private Key Public Key
signing verification
│ │
▼ ▼
AuthX ConsumersAuthX therefore provides a dedicated identity security boundary: credentials and signing authority remain inside AuthX, while DjangoPlay and other consumers verify and consume the resulting identity assertions without receiving AuthX's most sensitive secrets.