mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-12 22:10:25 +00:00
**Backport: !11936** - Go has a suite of small linters that helps with modernizing Go code by using newer functions and catching small mistakes, https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize. - Enable this linter in golangci-lint. - There's also [`go fix`](https://go.dev/blog/gofix), which is not yet released as a linter in golangci-lint: https://github.com/golangci/golangci-lint/pull/6385 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11949 Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-committed-by: Gusted <postmaster@gusted.xyz>
145 lines
3.8 KiB
Go
145 lines
3.8 KiB
Go
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package actions
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"forgejo.org/modules/jwtx"
|
|
"forgejo.org/modules/setting"
|
|
"forgejo.org/modules/web"
|
|
web_types "forgejo.org/modules/web/types"
|
|
actions_service "forgejo.org/services/actions"
|
|
"forgejo.org/services/context"
|
|
)
|
|
|
|
type oidcRoutes struct {
|
|
openIDConfiguration openIDConfiguration
|
|
jwks map[string][]map[string]string
|
|
}
|
|
|
|
type openIDConfiguration struct {
|
|
Issuer string `json:"issuer"`
|
|
JwksURI string `json:"jwks_uri"`
|
|
SubjectTypesSupported []string `json:"subject_types_supported"`
|
|
ResponseTypesSupported []string `json:"response_types_supported"`
|
|
ClaimsSupported []string `json:"claims_supported"`
|
|
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
|
|
ScopesSupported []string `json:"scopes_supported"`
|
|
}
|
|
|
|
type oidcContextKeyType struct{}
|
|
|
|
var oidcContextKey = oidcContextKeyType{}
|
|
|
|
// jwtSigningKey is the default signing key for JWTs.
|
|
var jwtSigningKey jwtx.SigningKey
|
|
|
|
// jwk is the JWK format of the jwtSigningKey.
|
|
var jwk map[string]string
|
|
|
|
type OIDCContext struct {
|
|
*context.Base
|
|
}
|
|
|
|
func InitOIDC() error {
|
|
var err error
|
|
jwtSigningKey, err = jwtx.InitAsymmetricSigningKey(setting.Actions.IDTokenSigningPrivateKeyFile, string(setting.Actions.IDTokenSigningAlgorithm))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
jwk, err = jwtSigningKey.ToJWK()
|
|
if err != nil {
|
|
return fmt.Errorf("Error getting JWK from default signing key: %v", err)
|
|
}
|
|
jwk["use"] = "sig"
|
|
|
|
return nil
|
|
}
|
|
|
|
func init() {
|
|
web.RegisterResponseStatusProvider[*OIDCContext](func(req *http.Request) web_types.ResponseStatusProvider {
|
|
return req.Context().Value(oidcContextKey).(*OIDCContext)
|
|
})
|
|
}
|
|
|
|
func OIDCContexter() func(next http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
|
base, baseCleanUp := context.NewBaseContext(resp, req)
|
|
defer baseCleanUp()
|
|
|
|
ctx := &OIDCContext{Base: base}
|
|
ctx.AppendContextValue(oidcContextKey, ctx)
|
|
|
|
next.ServeHTTP(ctx.Resp, ctx.Req)
|
|
})
|
|
}
|
|
}
|
|
|
|
func OIDCRoutes(prefix string) *web.Route {
|
|
m := web.NewRoute()
|
|
|
|
prefix = strings.TrimPrefix(prefix, "/")
|
|
|
|
// Standard claims
|
|
claimsSupported := []string{
|
|
"sub",
|
|
"aud",
|
|
"exp",
|
|
"iat",
|
|
"iss",
|
|
"nbf",
|
|
}
|
|
|
|
// Add custom claims by iterating over [actions_service.IDTokenCustomClaims]
|
|
// and inspecting the names of the json struct tags
|
|
rt := reflect.TypeFor[actions_service.IDTokenCustomClaims]()
|
|
|
|
for i := 0; i < rt.NumField(); i++ {
|
|
f := rt.Field(i)
|
|
v := strings.Split(f.Tag.Get("json"), ",")[0]
|
|
if v == "" || v == "-" {
|
|
continue
|
|
}
|
|
|
|
claimsSupported = append(claimsSupported, v)
|
|
}
|
|
|
|
o := &oidcRoutes{
|
|
openIDConfiguration: openIDConfiguration{
|
|
Issuer: setting.AppURL + prefix,
|
|
JwksURI: setting.AppURL + prefix + "/.well-known/keys",
|
|
SubjectTypesSupported: []string{"public"},
|
|
ResponseTypesSupported: []string{"id_token"},
|
|
ClaimsSupported: claimsSupported,
|
|
IDTokenSigningAlgValuesSupported: []string{string(setting.Actions.IDTokenSigningAlgorithm)},
|
|
ScopesSupported: []string{"openid"},
|
|
},
|
|
jwks: map[string][]map[string]string{
|
|
"keys": {
|
|
jwk,
|
|
},
|
|
},
|
|
}
|
|
|
|
m.Group("", func() {
|
|
m.Get("/keys", o.keys)
|
|
m.Get("/openid-configuration", o.configuration)
|
|
}, OIDCContexter())
|
|
|
|
return m
|
|
}
|
|
|
|
func (o *oidcRoutes) configuration(ctx *OIDCContext) {
|
|
ctx.JSON(http.StatusOK, o.openIDConfiguration)
|
|
}
|
|
|
|
func (o *oidcRoutes) keys(ctx *OIDCContext) {
|
|
ctx.JSON(http.StatusOK, o.jwks)
|
|
}
|