feat: add support for declaritive client secret configuration (#1619)

This commit is contained in:
Markus Schanz
2026-07-26 16:27:42 +02:00
committed by GitHub
parent a1b4e1d2b2
commit 9f559788a4
4 changed files with 48 additions and 6 deletions
+12 -2
View File
@@ -1,6 +1,8 @@
package controller
import (
"errors"
"io"
"net/http"
"strconv"
"strings"
@@ -234,14 +236,22 @@ func (oc *OidcController) updateClientHandler(c *gin.Context) {
// createClientSecretHandler godoc
// @Summary Create client secret
// @Description Generate a new secret for an OIDC client
// @Description Set or generate a new secret for an OIDC client
// @Tags OIDC
// @Accept json
// @Produce json
// @Param id path string true "Client ID"
// @Param payload body dto.OidcClientSecretDto false "Client secret"
// @Success 200 {object} object "{ \"secret\": \"string\" }"
// @Router /api/oidc/clients/{id}/secret [post]
func (oc *OidcController) createClientSecretHandler(c *gin.Context) {
secret, err := oc.oidcService.CreateClientSecret(c.Request.Context(), c.Param("id"))
var input dto.OidcClientSecretDto
if err := c.ShouldBindJSON(&input); err != nil && !errors.Is(err, io.EOF) {
_ = c.Error(err)
return
}
secret, err := oc.oidcService.CreateClientSecret(c.Request.Context(), c.Param("id"), input)
if err != nil {
_ = c.Error(err)
return
+4
View File
@@ -59,6 +59,10 @@ type OidcClientCreateDto struct {
ID string `json:"id" binding:"omitempty,client_id,min=2,max=128"`
}
type OidcClientSecretDto struct {
Secret string `json:"secret" binding:"omitempty,min=16,printascii"`
}
type OidcClientCredentialsDto struct {
FederatedIdentities []OidcClientFederatedIdentityDto `json:"federatedIdentities,omitempty"`
}
+7 -4
View File
@@ -266,7 +266,7 @@ func (s *OidcService) DeleteClient(ctx context.Context, clientID string) error {
return nil
}
func (s *OidcService) CreateClientSecret(ctx context.Context, clientID string) (string, error) {
func (s *OidcService) CreateClientSecret(ctx context.Context, clientID string, input dto.OidcClientSecretDto) (string, error) {
tx := s.db.Begin()
defer func() {
tx.Rollback()
@@ -281,9 +281,12 @@ func (s *OidcService) CreateClientSecret(ctx context.Context, clientID string) (
return "", err
}
clientSecret, err := utils.GenerateRandomAlphanumericString(32)
if err != nil {
return "", err
clientSecret := input.Secret
if clientSecret == "" {
clientSecret, err = utils.GenerateRandomAlphanumericString(32)
if err != nil {
return "", err
}
}
hashedSecret, err := bcrypt.GenerateFromPassword([]byte(clientSecret), bcrypt.DefaultCost)
@@ -7,6 +7,8 @@ import (
"strings"
"testing"
"golang.org/x/crypto/bcrypt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -499,6 +501,29 @@ func TestOidcService_CreateClient_withoutDescription(t *testing.T) {
assert.Empty(t, fetched.Description)
}
func TestOidcService_CreateClientSecret_withCustomSecret(t *testing.T) {
db := testutils.NewDatabaseForTest(t)
s, err := NewOidcService(db, nil, nil, nil, nil, nil)
require.NoError(t, err)
client := model.OidcClient{Name: "Test Client"}
err = db.Create(&client).Error
require.NoError(t, err)
customSecret := "custom-client-secret-with-a-minimum-length"
input := dto.OidcClientSecretDto{Secret: customSecret}
secret, err := s.CreateClientSecret(t.Context(), client.ID, input)
require.NoError(t, err)
assert.Equal(t, customSecret, secret)
var fetched model.OidcClient
err = db.First(&fetched, "id = ?", client.ID).Error
require.NoError(t, err)
require.NoError(t, bcrypt.CompareHashAndPassword([]byte(fetched.Secret), []byte(customSecret)))
}
func TestOidcService_UpdateClient_description(t *testing.T) {
db := testutils.NewDatabaseForTest(t)