Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69c506c9f9 | ||
|
|
dba718e87b |
@@ -52,18 +52,18 @@ func StripIncoming(r *http.Request) {
|
|||||||
func Inject(r *http.Request, claims jwt.MapClaims) {
|
func Inject(r *http.Request, claims jwt.MapClaims) {
|
||||||
r.Header.Set(HeaderAuthVerified, "true")
|
r.Header.Set(HeaderAuthVerified, "true")
|
||||||
|
|
||||||
if sub := claimString(claims, "sub"); sub != "" {
|
if sub := ClaimString(claims, "sub"); sub != "" {
|
||||||
r.Header.Set(HeaderUser, sub)
|
r.Header.Set(HeaderUser, sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
if roles := claimStringSlice(claims, "roles"); len(roles) > 0 {
|
if roles := ClaimStringSlice(claims, "roles"); len(roles) > 0 {
|
||||||
r.Header.Set(HeaderRoles, strings.Join(roles, ","))
|
r.Header.Set(HeaderRoles, strings.Join(roles, ","))
|
||||||
} else if perms := claimStringSlice(claims, "permissions"); len(perms) > 0 {
|
} else if perms := ClaimStringSlice(claims, "permissions"); len(perms) > 0 {
|
||||||
r.Header.Set(HeaderRoles, strings.Join(perms, ","))
|
r.Header.Set(HeaderRoles, strings.Join(perms, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
if azp := claimString(claims, "azp"); azp != "" {
|
if azp := ClaimString(claims, "azp"); azp != "" {
|
||||||
sub := claimString(claims, "sub")
|
sub := ClaimString(claims, "sub")
|
||||||
// Only set acting-service when azp differs from sub
|
// Only set acting-service when azp differs from sub
|
||||||
// (i.e., a service account acting, not the user themselves)
|
// (i.e., a service account acting, not the user themselves)
|
||||||
if azp != sub {
|
if azp != sub {
|
||||||
@@ -72,9 +72,9 @@ func Inject(r *http.Request, claims jwt.MapClaims) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// claimString extracts a string value from claims, returning "" if
|
// ClaimString extracts a string value from claims, returning "" if
|
||||||
// the key is missing or not a string.
|
// the key is missing or not a string.
|
||||||
func claimString(claims jwt.MapClaims, key string) string {
|
func ClaimString(claims jwt.MapClaims, key string) string {
|
||||||
val, ok := claims[key]
|
val, ok := claims[key]
|
||||||
if !ok || val == nil {
|
if !ok || val == nil {
|
||||||
return ""
|
return ""
|
||||||
@@ -86,10 +86,10 @@ func claimString(claims jwt.MapClaims, key string) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// claimStringSlice extracts a []string from claims. JWT libraries
|
// ClaimStringSlice extracts a []string from claims. JWT libraries
|
||||||
// deserialize JSON arrays as []interface{}, so each element is
|
// deserialize JSON arrays as []interface{}, so each element is
|
||||||
// type-asserted individually. Non-string elements are skipped.
|
// type-asserted individually. Non-string elements are skipped.
|
||||||
func claimStringSlice(claims jwt.MapClaims, key string) []string {
|
func ClaimStringSlice(claims jwt.MapClaims, key string) []string {
|
||||||
val, ok := claims[key]
|
val, ok := claims[key]
|
||||||
if !ok || val == nil {
|
if !ok || val == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -359,6 +359,24 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Inject identity headers for downstream services
|
// Inject identity headers for downstream services
|
||||||
identity.Inject(r, claims)
|
identity.Inject(r, claims)
|
||||||
|
|
||||||
|
// Audit trail: log successful JWT authentication
|
||||||
|
auditFields := map[string]string{
|
||||||
|
"path": r.URL.Path,
|
||||||
|
"method": r.Method,
|
||||||
|
}
|
||||||
|
if sub := identity.ClaimString(claims, "sub"); sub != "" {
|
||||||
|
auditFields["subject"] = sub
|
||||||
|
}
|
||||||
|
if azp := identity.ClaimString(claims, "azp"); azp != "" {
|
||||||
|
auditFields["acting_party"] = azp
|
||||||
|
}
|
||||||
|
if roles := identity.ClaimStringSlice(claims, "roles"); len(roles) > 0 {
|
||||||
|
auditFields["roles"] = strings.Join(roles, ",")
|
||||||
|
} else if perms := identity.ClaimStringSlice(claims, "permissions"); len(perms) > 0 {
|
||||||
|
auditFields["permissions"] = strings.Join(perms, ",")
|
||||||
|
}
|
||||||
|
logging.Infof("auth ok", auditFields)
|
||||||
|
|
||||||
// Check required capability if configured
|
// Check required capability if configured
|
||||||
if h.config.Auth.RequiredCapability != "" {
|
if h.config.Auth.RequiredCapability != "" {
|
||||||
if !h.jwtValidator.CheckPermissions(claims, h.config.Auth.RequiredCapability, "*") {
|
if !h.jwtValidator.CheckPermissions(claims, h.config.Auth.RequiredCapability, "*") {
|
||||||
|
|||||||
+3
-3
@@ -15,9 +15,9 @@ data:
|
|||||||
enabled: true
|
enabled: true
|
||||||
issuer: "https://authentik.riotpiao.com/application/o/api-gw/"
|
issuer: "https://authentik.riotpiao.com/application/o/api-gw/"
|
||||||
audience: "api-gw"
|
audience: "api-gw"
|
||||||
jwksUrl: "http://authentik-server.iam.svc.cluster.local:9000/application/o/api-gw/jwks/"
|
jwksUrl: "http://authentik-server.iam.svc.cluster.local/application/o/api-gw/jwks/"
|
||||||
requiredCapability: "llm:inference"
|
requiredCapability: "llm:inference"
|
||||||
tokenUrl: "http://authentik-server.iam.svc.cluster.local:9000/application/o/token/"
|
tokenUrl: "http://authentik-server.iam.svc.cluster.local/application/o/token/"
|
||||||
clientId: "api-gw"
|
clientId: "api-gw"
|
||||||
|
|
||||||
# Routes: standard HTTP proxy routes (not LLM-specific)
|
# Routes: standard HTTP proxy routes (not LLM-specific)
|
||||||
@@ -137,7 +137,7 @@ data:
|
|||||||
|
|
||||||
- serviceName: iam
|
- serviceName: iam
|
||||||
upstream:
|
upstream:
|
||||||
url: http://authentik-server.iam.svc.cluster.local:9000
|
url: http://authentik-server.iam.svc.cluster.local:80
|
||||||
timeoutSeconds: 30
|
timeoutSeconds: 30
|
||||||
auth:
|
auth:
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
Reference in New Issue
Block a user