fix: accept multiple OIDC audiences for cross-client auth
- Changed oidc_audience (string) to oidc_audiences (list) - Now accepts tokens with audience: core-api, tatlock-ui, tatlock - Fixes environment endpoint returning "default" user when using tatlock-ui token (audience mismatch was causing JWT claims error) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
c1f16d44e5
commit
6ce34cc016
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.10.5] - 2026-01-07
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **OIDC audience mismatch** - Accept tokens from multiple clients
|
||||||
|
- Changed `oidc_audience` (string) to `oidc_audiences` (list)
|
||||||
|
- Now accepts tokens with audience: `core-api`, `tatlock-ui`, or `tatlock`
|
||||||
|
- Fixes environment endpoint returning "default" user instead of authenticated username
|
||||||
|
|
||||||
## [1.10.4] - 2026-01-07
|
## [1.10.4] - 2026-01-07
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "core-api"
|
name = "core-api"
|
||||||
version = "1.10.4"
|
version = "1.10.5"
|
||||||
description = "Core Code API - Infrastructure management and tools API"
|
description = "Core Code API - Infrastructure management and tools API"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
+6
-6
@@ -23,16 +23,16 @@ class OIDCConfig:
|
|||||||
# These will be set from environment variables in config.py
|
# These will be set from environment variables in config.py
|
||||||
self.enabled = False
|
self.enabled = False
|
||||||
self.issuer = ""
|
self.issuer = ""
|
||||||
self.audience = ""
|
self.audiences: list[str] = []
|
||||||
self.jwks_uri = ""
|
self.jwks_uri = ""
|
||||||
|
|
||||||
def configure(self, enabled: bool, issuer: str, audience: str):
|
def configure(self, enabled: bool, issuer: str, audiences: list[str]):
|
||||||
"""Configure OIDC settings"""
|
"""Configure OIDC settings"""
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.issuer = issuer
|
self.issuer = issuer
|
||||||
self.audience = audience
|
self.audiences = audiences
|
||||||
self.jwks_uri = f"{issuer.rstrip('/')}/jwks/"
|
self.jwks_uri = f"{issuer.rstrip('/')}/jwks/"
|
||||||
logger.info(f"OIDC configured: enabled={enabled}, issuer={issuer}")
|
logger.info(f"OIDC configured: enabled={enabled}, issuer={issuer}, audiences={audiences}")
|
||||||
|
|
||||||
|
|
||||||
# Global OIDC config instance
|
# Global OIDC config instance
|
||||||
@@ -129,12 +129,12 @@ async def get_current_user(
|
|||||||
logger.warning(f"No matching key found for kid: {kid}")
|
logger.warning(f"No matching key found for kid: {kid}")
|
||||||
raise HTTPException(status_code=401, detail="Invalid token key")
|
raise HTTPException(status_code=401, detail="Invalid token key")
|
||||||
|
|
||||||
# Verify and decode token
|
# Verify and decode token (accepts any of the configured audiences)
|
||||||
payload = jwt.decode(
|
payload = jwt.decode(
|
||||||
token,
|
token,
|
||||||
rsa_key,
|
rsa_key,
|
||||||
algorithms=["RS256"],
|
algorithms=["RS256"],
|
||||||
audience=oidc_config.audience,
|
audience=oidc_config.audiences,
|
||||||
issuer=oidc_config.issuer,
|
issuer=oidc_config.issuer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -84,7 +84,8 @@ class Settings(BaseSettings):
|
|||||||
# OIDC Authentication (Authentik)
|
# OIDC Authentication (Authentik)
|
||||||
oidc_enabled: bool = False # Set to True to require authentication
|
oidc_enabled: bool = False # Set to True to require authentication
|
||||||
oidc_issuer: str = "https://auth.schweitz.net/application/o/core-api/"
|
oidc_issuer: str = "https://auth.schweitz.net/application/o/core-api/"
|
||||||
oidc_audience: str = "core-api"
|
# Accept tokens from multiple clients (core-api, tatlock-ui, tatlock)
|
||||||
|
oidc_audiences: list[str] = ["core-api", "tatlock-ui", "tatlock"]
|
||||||
|
|
||||||
# Authentik API (for token validation and user management)
|
# Authentik API (for token validation and user management)
|
||||||
# Must use domain name (not IP) when AUTHENTIK_COOKIE_DOMAIN is set
|
# Must use domain name (not IP) when AUTHENTIK_COOKIE_DOMAIN is set
|
||||||
|
|||||||
@@ -64,16 +64,16 @@ class OIDCConfig:
|
|||||||
# These will be set from environment variables in config.py
|
# These will be set from environment variables in config.py
|
||||||
self.enabled = False
|
self.enabled = False
|
||||||
self.issuer = ""
|
self.issuer = ""
|
||||||
self.audience = ""
|
self.audiences: list[str] = []
|
||||||
self.jwks_uri = ""
|
self.jwks_uri = ""
|
||||||
|
|
||||||
def configure(self, enabled: bool, issuer: str, audience: str):
|
def configure(self, enabled: bool, issuer: str, audiences: list[str]):
|
||||||
"""Configure OIDC settings"""
|
"""Configure OIDC settings"""
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.issuer = issuer
|
self.issuer = issuer
|
||||||
self.audience = audience
|
self.audiences = audiences
|
||||||
self.jwks_uri = f"{issuer.rstrip('/')}/jwks/"
|
self.jwks_uri = f"{issuer.rstrip('/')}/jwks/"
|
||||||
logger.info(f"OIDC configured: enabled={enabled}, issuer={issuer}")
|
logger.info(f"OIDC configured: enabled={enabled}, issuer={issuer}, audiences={audiences}")
|
||||||
|
|
||||||
|
|
||||||
# Global OIDC config instance
|
# Global OIDC config instance
|
||||||
@@ -177,12 +177,12 @@ async def get_current_user(
|
|||||||
logger.warning(f"No matching key found for kid: {kid}")
|
logger.warning(f"No matching key found for kid: {kid}")
|
||||||
raise HTTPException(status_code=401, detail="Invalid token key")
|
raise HTTPException(status_code=401, detail="Invalid token key")
|
||||||
|
|
||||||
# Verify and decode token
|
# Verify and decode token (accepts any of the configured audiences)
|
||||||
payload = jwt.decode(
|
payload = jwt.decode(
|
||||||
token,
|
token,
|
||||||
rsa_key,
|
rsa_key,
|
||||||
algorithms=["RS256"],
|
algorithms=["RS256"],
|
||||||
audience=oidc_config.audience,
|
audience=oidc_config.audiences,
|
||||||
issuer=oidc_config.issuer,
|
issuer=oidc_config.issuer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ class Settings(BaseSettings):
|
|||||||
# OIDC Authentication (Authentik)
|
# OIDC Authentication (Authentik)
|
||||||
oidc_enabled: bool = False
|
oidc_enabled: bool = False
|
||||||
oidc_issuer: str = "https://auth.schweitz.net/application/o/core-api/"
|
oidc_issuer: str = "https://auth.schweitz.net/application/o/core-api/"
|
||||||
oidc_audience: str = "core-api"
|
# Accept tokens from multiple clients (core-api, tatlock-ui, tatlock)
|
||||||
|
oidc_audiences: list[str] = ["core-api", "tatlock-ui", "tatlock"]
|
||||||
|
|
||||||
# Authentik API (for token validation and user management)
|
# Authentik API (for token validation and user management)
|
||||||
authentik_url: str = "https://auth.schweitz.net"
|
authentik_url: str = "https://auth.schweitz.net"
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ def initialize_oidc(settings: Settings) -> None:
|
|||||||
auth_oidc_config.configure(
|
auth_oidc_config.configure(
|
||||||
enabled=settings.oidc_enabled,
|
enabled=settings.oidc_enabled,
|
||||||
issuer=settings.oidc_issuer,
|
issuer=settings.oidc_issuer,
|
||||||
audience=settings.oidc_audience
|
audiences=settings.oidc_audiences
|
||||||
)
|
)
|
||||||
|
|
||||||
domains_oidc_config.configure(
|
domains_oidc_config.configure(
|
||||||
enabled=settings.oidc_enabled,
|
enabled=settings.oidc_enabled,
|
||||||
issuer=settings.oidc_issuer,
|
issuer=settings.oidc_issuer,
|
||||||
audience=settings.oidc_audience
|
audiences=settings.oidc_audiences
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.oidc_enabled:
|
if settings.oidc_enabled:
|
||||||
|
|||||||
Reference in New Issue
Block a user