fix: oidc review comments

This commit is contained in:
Stavros
2026-01-25 18:32:14 +02:00
parent cf1a613229
commit 8af233b78d
3 changed files with 38 additions and 20 deletions

View File

@@ -94,6 +94,7 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er
PrivateKeyPath: app.config.OIDC.PrivateKeyPath, PrivateKeyPath: app.config.OIDC.PrivateKeyPath,
PublicKeyPath: app.config.OIDC.PublicKeyPath, PublicKeyPath: app.config.OIDC.PublicKeyPath,
Issuer: app.config.AppURL, Issuer: app.config.AppURL,
SessionExpiry: app.config.Auth.SessionExpiry,
}, queries) }, queries)
err = oidcService.Init() err = oidcService.Init()

View File

@@ -148,13 +148,15 @@ func (controller *OIDCController) Authorize(c *gin.Context) {
return return
} }
// We also need a snapshot of the user that authorized this // We also need a snapshot of the user that authorized this (skip if no openid scope)
err = controller.oidc.StoreUserinfo(c, sub, userContext, req) if slices.Contains(strings.Split(req.Scope, " "), "openid") {
err = controller.oidc.StoreUserinfo(c, sub, userContext, req)
if err != nil { if err != nil {
tlog.App.Error().Err(err).Msg("Failed to insert user info into database") tlog.App.Error().Err(err).Msg("Failed to insert user info into database")
controller.authorizeError(c, err, "Failed to store user info", "Failed to store user info", req.RedirectURI, "server_error", req.State) controller.authorizeError(c, err, "Failed to store user info", "Failed to store user info", req.RedirectURI, "server_error", req.State)
return return
}
} }
queries, err := query.Values(AuthorizeCallback{ queries, err := query.Values(AuthorizeCallback{
@@ -315,6 +317,15 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
return return
} }
// If we don't have the openid scope, return an error
if !slices.Contains(strings.Split(entry.Scope, ","), "openid") {
tlog.App.Warn().Msg("OIDC userinfo accessed without openid scope")
c.JSON(401, gin.H{
"error": "invalid_request",
})
return
}
user, err := controller.oidc.GetUserinfo(c, entry.Sub) user, err := controller.oidc.GetUserinfo(c, entry.Sub)
if err != nil { if err != nil {
@@ -325,15 +336,6 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
return return
} }
// If we don't have the openid scope, return an error
if !slices.Contains(strings.Split(entry.Scope, ","), "openid") {
tlog.App.Warn().Msg("OIDC userinfo accessed without openid scope")
c.JSON(401, gin.H{
"error": "invalid_request",
})
return
}
c.JSON(200, controller.oidc.CompileUserinfo(user, entry.Scope)) c.JSON(200, controller.oidc.CompileUserinfo(user, entry.Scope))
} }
@@ -362,7 +364,7 @@ func (controller *OIDCController) authorizeError(c *gin.Context, err error, reas
c.JSON(200, gin.H{ c.JSON(200, gin.H{
"status": 200, "status": 200,
"redirect_uri": fmt.Sprintf("%s/?%s", callback, queries.Encode()), "redirect_uri": fmt.Sprintf("%s?%s", callback, queries.Encode()),
}) })
return return
} }

View File

@@ -69,6 +69,7 @@ type OIDCServiceConfig struct {
PrivateKeyPath string PrivateKeyPath string
PublicKeyPath string PublicKeyPath string
Issuer string Issuer string
SessionExpiry int
} }
type OIDCService struct { type OIDCService struct {
@@ -123,6 +124,9 @@ func (service *OIDCService) Init() error {
return err return err
} }
der := x509.MarshalPKCS1PrivateKey(privateKey) der := x509.MarshalPKCS1PrivateKey(privateKey)
if der == nil {
return errors.New("failed to marshal private key")
}
encoded := pem.EncodeToMemory(&pem.Block{ encoded := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY", Type: "RSA PRIVATE KEY",
Bytes: der, Bytes: der,
@@ -134,6 +138,9 @@ func (service *OIDCService) Init() error {
service.privateKey = privateKey service.privateKey = privateKey
} else { } else {
block, _ := pem.Decode(fprivateKey) block, _ := pem.Decode(fprivateKey)
if block == nil {
return errors.New("failed to decode private key")
}
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil { if err != nil {
return err return err
@@ -150,6 +157,9 @@ func (service *OIDCService) Init() error {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
publicKey := service.privateKey.Public() publicKey := service.privateKey.Public()
der := x509.MarshalPKCS1PublicKey(publicKey.(*rsa.PublicKey)) der := x509.MarshalPKCS1PublicKey(publicKey.(*rsa.PublicKey))
if der == nil {
return errors.New("failed to marshal public key")
}
encoded := pem.EncodeToMemory(&pem.Block{ encoded := pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY", Type: "RSA PUBLIC KEY",
Bytes: der, Bytes: der,
@@ -161,6 +171,9 @@ func (service *OIDCService) Init() error {
service.publicKey = publicKey service.publicKey = publicKey
} else { } else {
block, _ := pem.Decode(fpublicKey) block, _ := pem.Decode(fpublicKey)
if block == nil {
return errors.New("failed to decode public key")
}
publicKey, err := x509.ParsePKCS1PublicKey(block.Bytes) publicKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil { if err != nil {
return err return err
@@ -316,9 +329,7 @@ func (service *OIDCService) GetCodeEntry(c *gin.Context, codeHash string) (repos
func (service *OIDCService) generateIDToken(client config.OIDCClientConfig, sub string) (string, error) { func (service *OIDCService) generateIDToken(client config.OIDCClientConfig, sub string) (string, error) {
createdAt := time.Now().Unix() createdAt := time.Now().Unix()
expiresAt := time.Now().Add(time.Duration(service.config.SessionExpiry) * time.Second).Unix()
// TODO: This should probably be user-configured if refresh logic does not exist
expiresAt := time.Now().Add(time.Duration(1) * time.Hour).Unix()
claims := jws.ClaimSet{ claims := jws.ClaimSet{
Iss: service.issuer, Iss: service.issuer,
@@ -432,7 +443,11 @@ func (service *OIDCService) CompileUserinfo(user repository.OidcUserinfo, scope
} }
if slices.Contains(scopes, "groups") { if slices.Contains(scopes, "groups") {
userInfo.Groups = strings.Split(user.Groups, ",") if user.Groups != "" {
userInfo.Groups = strings.Split(user.Groups, ",")
} else {
userInfo.Groups = []string{}
}
} }
return userInfo return userInfo