From 17bd1e09098ce81205af3c45a95a7909e16030f8 Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Fri, 18 Apr 2025 02:21:29 +0200 Subject: [PATCH 01/13] Add api support support for OAuth2 authentication --- modules/structs/auth_oauth2.go | 54 ++++++++++++++++++++ routers/api/v1/admin/auth_oauth.go | 81 ++++++++++++++++++++++++++++++ routers/api/v1/api.go | 4 ++ 3 files changed, 139 insertions(+) create mode 100644 modules/structs/auth_oauth2.go create mode 100644 routers/api/v1/admin/auth_oauth.go diff --git a/modules/structs/auth_oauth2.go b/modules/structs/auth_oauth2.go new file mode 100644 index 0000000000000..af1f87facda93 --- /dev/null +++ b/modules/structs/auth_oauth2.go @@ -0,0 +1,54 @@ +// Copyright 2015 The Gogs Authors. All rights reserved. +// Copyright 2019 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package structs + +// CreateUserOption create user options +type CreateAuthOauth2Option struct { + AuthenticationName string `json:"authentication_name" binding:"Required"` + ProviderIconURL string `json:"provider_icon_url"` + ProviderClientID string `json:"provider_client_id" binding:"Required"` + ProviderClientSecret string `json:"provider_client_secret" binding:"Required"` + ProviderAutoDiscoveryURL string `json:"provider_auto_discovery_url" binding:"Required"` + + SkipLocal2FA bool `json:"skip_local_2fa"` + AdditionalScopes string `json:"additional_scopes"` + RequiredClaimName string `json:"required_claim_name"` + RequiredClaimValue string `json:"required_claim_value"` + + ClaimNameProvidingGroupNameForSource string `json:"claim_name_providingGroupNameForSource"` + GroupClaimValueForAdministratorUsers string `json:"group_claim_value_for_administrator_users"` + GroupClaimValueForRestrictedUsers string `json:"group_claim_value_for_restricted_users"` + MapClaimedGroupsToOrganizationTeams string `json:"map_claimed_groups_to_organization_teams"` + + RemoveUsersFromSyncronizedTeams bool `json:"RemoveUsersFromSyncronizedTeams"` + EnableUserSyncronization bool `json:"EnableUserSyncronization"` + AuthenticationSourceIsActive bool `json:"AuthenticationSourceIsActive"` +} + +// EditUserOption edit user options +type EditAuthOauth2Option struct { + // // required: true + SourceID int64 `json:"source_id"` + + AuthenticationName string `json:"authentication_name" binding:"Required"` + ProviderIconURL string `json:"provider_icon_url"` + ProviderClientID string `json:"provider_client_id" binding:"Required"` + ProviderClientSecret string `json:"provider_client_secret" binding:"Required"` + ProviderAutoDiscoveryURL string `json:"provider_auto_discovery_url" binding:"Required"` + + SkipLocal2FA bool `json:"skip_local_2fa"` + AdditionalScopes string `json:"additional_scopes"` + RequiredClaimName string `json:"required_claim_name"` + RequiredClaimValue string `json:"required_claim_value"` + + ClaimNameProvidingGroupNameForSource string `json:"claim_name_providingGroupNameForSource"` + GroupClaimValueForAdministratorUsers string `json:"group_claim_value_for_administrator_users"` + GroupClaimValueForRestrictedUsers string `json:"group_claim_value_for_restricted_users"` + MapClaimedGroupsToOrganizationTeams string `json:"map_claimed_groups_to_organization_teams"` + + RemoveUsersFromSyncronizedTeams bool `json:"RemoveUsersFromSyncronizedTeams"` + EnableUserSyncronization bool `json:"EnableUserSyncronization"` + AuthenticationSourceIsActive bool `json:"AuthenticationSourceIsActive"` +} diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go new file mode 100644 index 0000000000000..32422ebedfef9 --- /dev/null +++ b/routers/api/v1/admin/auth_oauth.go @@ -0,0 +1,81 @@ +// Copyright 2015 The Gogs Authors. All rights reserved. +// Copyright 2019 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package admin + +import ( + "fmt" + "net/http" + "net/url" + + auth_model "code.gitea.io/gitea/models/auth" + api "code.gitea.io/gitea/modules/structs" + + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/auth/source/oauth2" + "code.gitea.io/gitea/services/context" +) + +// CreateOauthAuth create a new external authentication for oauth2 +func CreateOauthAuth(ctx *context.APIContext) { + form := web.GetForm(ctx).(*api.CreateAuthOauth2Option) + + var scopes []string + // for _, s := range strings.Split(form.Oauth2Scopes, ",") { + // s = strings.TrimSpace(s) + // if s != "" { + // scopes = append(scopes, s) + // } + // } + + discoveryURL, err := url.Parse(form.ProviderAutoDiscoveryURL) + if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") { + fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", form.ProviderAutoDiscoveryURL) + + // todo: implement handling + } + + config := &oauth2.Source{ + Provider: "openidConnect", + ClientID: form.ProviderClientID, + ClientSecret: form.ProviderClientSecret, + OpenIDConnectAutoDiscoveryURL: form.ProviderAutoDiscoveryURL, + CustomURLMapping: nil, + IconURL: form.ProviderIconURL, + Scopes: scopes, + RequiredClaimName: form.RequiredClaimName, + RequiredClaimValue: form.RequiredClaimValue, + SkipLocalTwoFA: form.SkipLocal2FA, + + GroupClaimName: form.ClaimNameProvidingGroupNameForSource, + RestrictedGroup: form.GroupClaimValueForRestrictedUsers, + AdminGroup: form.GroupClaimValueForAdministratorUsers, + GroupTeamMap: form.MapClaimedGroupsToOrganizationTeams, + GroupTeamMapRemoval: form.RemoveUsersFromSyncronizedTeams, + } + + auth_model.CreateSource(ctx, &auth_model.Source{ + Type: auth_model.OAuth2, + Name: form.AuthenticationName, + IsActive: true, + Cfg: config, + }) + + ctx.Status(http.StatusCreated) + + // ctx.JSON(http.StatusCreated, convert.ToUser(ctx, u, ctx.Doer)) +} + +// EditOauthAuth api for modifying a authentication method +func EditOauthAuth(ctx *context.APIContext) { +} + +// DeleteOauthAuth api for deleting a authentication method +func DeleteOauthAuth(ctx *context.APIContext) { +} + +// // SearchOauthAuth API for getting information of the configured authentication methods according the filter conditions +func SearchOauthAuth(ctx *context.APIContext) { + +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index b9b590725b42b..d1f3c8dd9b464 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1649,6 +1649,10 @@ func Routes() *web.Router { }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership(), checkTokenPublicOnly()) m.Group("/admin", func() { + m.Group("/identity-auth", func() { + m.Post("/new", admin.CreateOauthAuth) + }) + m.Group("/cron", func() { m.Get("", admin.ListCronTasks) m.Post("/{task}", admin.PostCronTask) From c2f5544d733ba5314cbaa72573a7d1e667ac9dd7 Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Fri, 18 Apr 2025 11:25:12 +0200 Subject: [PATCH 02/13] Add OAuth2 provider listing and conversion functions --- modules/structs/auth_oauth2.go | 6 +++++ routers/api/v1/admin/auth_oauth.go | 21 +++++++++++++++++ routers/api/v1/api.go | 1 + services/convert/auth_oauth.go | 37 ++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 services/convert/auth_oauth.go diff --git a/modules/structs/auth_oauth2.go b/modules/structs/auth_oauth2.go index af1f87facda93..93dfabffb6892 100644 --- a/modules/structs/auth_oauth2.go +++ b/modules/structs/auth_oauth2.go @@ -4,6 +4,12 @@ package structs +type AuthOauth2Option struct { + SourceID int64 `json:"source_id"` + AuthenticationName string `json:"authentication_name" binding:"Required"` + ProviderIconURL string `json:"provider_icon_url"` +} + // CreateUserOption create user options type CreateAuthOauth2Option struct { AuthenticationName string `json:"authentication_name" binding:"Required"` diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index 32422ebedfef9..ac563707bd0c7 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -11,10 +11,14 @@ import ( auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/convert" ) // CreateOauthAuth create a new external authentication for oauth2 @@ -77,5 +81,22 @@ func DeleteOauthAuth(ctx *context.APIContext) { // // SearchOauthAuth API for getting information of the configured authentication methods according the filter conditions func SearchOauthAuth(ctx *context.APIContext) { + listOptions := utils.GetListOptions(ctx) + authSources, maxResults, err := db.FindAndCount[auth.Source](ctx, auth.FindSourcesOptions{}) + // fmt.Printf("Count: %d, models: %v, err: %v", count, models[0].Name, err) + + if err != nil { + ctx.APIErrorInternal(err) + return + } + + results := make([]*api.AuthOauth2Option, len(authSources)) + for i := range authSources { + results[i] = convert.ToOauthProvider(ctx, authSources[i]) + } + + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.SetTotalCountHeader(maxResults) + ctx.JSON(http.StatusOK, &results) } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index d1f3c8dd9b464..bdd25700a33a0 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1650,6 +1650,7 @@ func Routes() *web.Router { m.Group("/admin", func() { m.Group("/identity-auth", func() { + m.Get("", admin.SearchOauthAuth) m.Post("/new", admin.CreateOauthAuth) }) diff --git a/services/convert/auth_oauth.go b/services/convert/auth_oauth.go new file mode 100644 index 0000000000000..c2f12f19a351f --- /dev/null +++ b/services/convert/auth_oauth.go @@ -0,0 +1,37 @@ +// // Copyright 2020 The Gitea Authors. All rights reserved. +// // SPDX-License-Identifier: MIT + +package convert + +import ( + "context" + + auth_model "code.gitea.io/gitea/models/auth" + api "code.gitea.io/gitea/modules/structs" +) + +// ToUser convert user_model.User to api.User +// if doer is set, private information is added if the doer has the permission to see it +func ToOauthProvider(ctx context.Context, provider *auth_model.Source) *api.AuthOauth2Option { + if provider == nil { + return nil + } + + return toOauthProvider(ctx, provider) +} + +// ToUsers convert list of user_model.User to list of api.User +func ToOauthProviders(ctx context.Context, provider []*auth_model.Source) []*api.AuthOauth2Option { + result := make([]*api.AuthOauth2Option, len(provider)) + for i := range provider { + result[i] = ToOauthProvider(ctx, provider[i]) + } + return result +} + +func toOauthProvider(ctx context.Context, provider *auth_model.Source) *api.AuthOauth2Option { + return &api.AuthOauth2Option{ + SourceID: provider.ID, + AuthenticationName: provider.Name, + } +} From c830bc13e4f4aeddfd769acddaee31b80f6ea35a Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Sat, 19 Apr 2025 00:01:37 +0200 Subject: [PATCH 03/13] Refactor AuthOauth2Option structure and improve error handling in CreateOauthAuth --- modules/structs/auth_oauth2.go | 8 ++++++-- routers/api/v1/admin/auth_oauth.go | 7 ++----- services/convert/auth_oauth.go | 7 ++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/modules/structs/auth_oauth2.go b/modules/structs/auth_oauth2.go index 93dfabffb6892..4ba1e46de5022 100644 --- a/modules/structs/auth_oauth2.go +++ b/modules/structs/auth_oauth2.go @@ -5,9 +5,13 @@ package structs type AuthOauth2Option struct { - SourceID int64 `json:"source_id"` + ID int64 `json:"id"` AuthenticationName string `json:"authentication_name" binding:"Required"` - ProviderIconURL string `json:"provider_icon_url"` + Type int `json:"type"` + TypeName string `json:"type_name"` + + IsActive bool `json:"is_active"` + IsSyncEnabled bool `json:"is_sync_enabled"` } // CreateUserOption create user options diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index ac563707bd0c7..247d173f704e1 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -35,9 +35,8 @@ func CreateOauthAuth(ctx *context.APIContext) { discoveryURL, err := url.Parse(form.ProviderAutoDiscoveryURL) if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") { - fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", form.ProviderAutoDiscoveryURL) - - // todo: implement handling + _ = fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", form.ProviderAutoDiscoveryURL) + ctx.HTTPError(http.StatusBadRequest, fmt.Sprintf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", form.ProviderAutoDiscoveryURL)) } config := &oauth2.Source{ @@ -67,8 +66,6 @@ func CreateOauthAuth(ctx *context.APIContext) { }) ctx.Status(http.StatusCreated) - - // ctx.JSON(http.StatusCreated, convert.ToUser(ctx, u, ctx.Doer)) } // EditOauthAuth api for modifying a authentication method diff --git a/services/convert/auth_oauth.go b/services/convert/auth_oauth.go index c2f12f19a351f..a86c0fc9a7808 100644 --- a/services/convert/auth_oauth.go +++ b/services/convert/auth_oauth.go @@ -31,7 +31,12 @@ func ToOauthProviders(ctx context.Context, provider []*auth_model.Source) []*api func toOauthProvider(ctx context.Context, provider *auth_model.Source) *api.AuthOauth2Option { return &api.AuthOauth2Option{ - SourceID: provider.ID, + ID: provider.ID, AuthenticationName: provider.Name, + Type: provider.Type.Int(), + TypeName: provider.Type.String(), + + IsActive: provider.IsActive, + IsSyncEnabled: provider.IsSyncEnabled, } } From 25425adf29ea28431635df356998062181131da6 Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Sat, 19 Apr 2025 00:36:54 +0200 Subject: [PATCH 04/13] Implement DeleteOauthAuth API and refactor source deletion logic --- models/auth/source.go | 7 ++++++- routers/api/v1/admin/auth_oauth.go | 15 +++++++++++++++ routers/api/v1/api.go | 7 +++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/models/auth/source.go b/models/auth/source.go index a3a250cd91d7c..fe121fb9194a8 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -235,13 +235,18 @@ func CreateSource(ctx context.Context, source *Source) error { err = registerableSource.RegisterSource() if err != nil { // remove the AuthSource in case of errors while registering configuration - if _, err := db.GetEngine(ctx).ID(source.ID).Delete(new(Source)); err != nil { + if err := DeleteSource(ctx, source.ID); err != nil { log.Error("CreateSource: Error while wrapOpenIDConnectInitializeError: %v", err) } } return err } +func DeleteSource(ctx context.Context, id int64) error { + _, err := db.GetEngine(ctx).ID(id).Delete(new(Source)) + return err +} + type FindSourcesOptions struct { db.ListOptions IsActive optional.Option[bool] diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index 247d173f704e1..73ffb90af3d41 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -8,6 +8,7 @@ import ( "fmt" "net/http" "net/url" + "strconv" auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" @@ -74,6 +75,20 @@ func EditOauthAuth(ctx *context.APIContext) { // DeleteOauthAuth api for deleting a authentication method func DeleteOauthAuth(ctx *context.APIContext) { + oauthIdString := ctx.PathParam("id") + oauthID, oauthIdErr := strconv.Atoi(oauthIdString) + if oauthIdErr != nil { + ctx.APIErrorInternal(oauthIdErr) + } + + err := auth_model.DeleteSource(ctx, int64(oauthID)) + + if err != nil { + ctx.APIErrorInternal(err) + return + } + + ctx.Status(http.StatusOK) } // // SearchOauthAuth API for getting information of the configured authentication methods according the filter conditions diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index bdd25700a33a0..06c49a3d6d4dc 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1650,8 +1650,11 @@ func Routes() *web.Router { m.Group("/admin", func() { m.Group("/identity-auth", func() { - m.Get("", admin.SearchOauthAuth) - m.Post("/new", admin.CreateOauthAuth) + m.Group("oauth", func() { + m.Get("", admin.SearchOauthAuth) + m.Delete("/{id}", admin.DeleteOauthAuth) + m.Post("/new", admin.CreateOauthAuth) + }) }) m.Group("/cron", func() { From 62dc4c2da36877803a56c7be0b6dd755a3629c03 Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Sat, 19 Apr 2025 01:47:57 +0200 Subject: [PATCH 05/13] Add PUT endpoint for creating new OAuth2 authentication and clean up routing --- routers/api/v1/admin/auth_oauth.go | 1 + routers/api/v1/api.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index 73ffb90af3d41..3d9327fe5c4db 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -26,6 +26,7 @@ import ( func CreateOauthAuth(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.CreateAuthOauth2Option) + // ??? todo: what should I do here? var scopes []string // for _, s := range strings.Split(form.Oauth2Scopes, ",") { // s = strings.TrimSpace(s) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 06c49a3d6d4dc..984095d070233 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1650,10 +1650,10 @@ func Routes() *web.Router { m.Group("/admin", func() { m.Group("/identity-auth", func() { - m.Group("oauth", func() { + m.Group("/oauth", func() { m.Get("", admin.SearchOauthAuth) + m.Put("/new", bind(api.CreateAuthOauth2Option{}), admin.CreateOauthAuth) m.Delete("/{id}", admin.DeleteOauthAuth) - m.Post("/new", admin.CreateOauthAuth) }) }) From 4787ea4bbf3b05eae19267396daf94d931a804ba Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Sat, 19 Apr 2025 02:18:22 +0200 Subject: [PATCH 06/13] Refactor SearchOauthAuth to use auth_model for source retrieval --- routers/api/v1/admin/auth_oauth.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index 3d9327fe5c4db..2613f549622d1 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -11,12 +11,10 @@ import ( "strconv" auth_model "code.gitea.io/gitea/models/auth" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/routers/api/v1/utils" - - "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" @@ -96,7 +94,7 @@ func DeleteOauthAuth(ctx *context.APIContext) { func SearchOauthAuth(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) - authSources, maxResults, err := db.FindAndCount[auth.Source](ctx, auth.FindSourcesOptions{}) + authSources, maxResults, err := db.FindAndCount[auth_model.Source](ctx, auth_model.FindSourcesOptions{}) // fmt.Printf("Count: %d, models: %v, err: %v", count, models[0].Name, err) if err != nil { From cb0e0cef8664a21b4b843a43c5448e8bfa221fe4 Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Sat, 19 Apr 2025 02:35:20 +0200 Subject: [PATCH 07/13] Refactor OAuth authentication methods for improved error handling and code clarity --- routers/api/v1/admin/auth_oauth.go | 18 ++++++++++-------- services/convert/auth_oauth.go | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index 2613f549622d1..8d03e5e961ec0 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -58,13 +58,18 @@ func CreateOauthAuth(ctx *context.APIContext) { GroupTeamMapRemoval: form.RemoveUsersFromSyncronizedTeams, } - auth_model.CreateSource(ctx, &auth_model.Source{ + createErr := auth_model.CreateSource(ctx, &auth_model.Source{ Type: auth_model.OAuth2, Name: form.AuthenticationName, IsActive: true, Cfg: config, }) + if createErr != nil { + ctx.APIErrorInternal(createErr) + return + } + ctx.Status(http.StatusCreated) } @@ -74,14 +79,13 @@ func EditOauthAuth(ctx *context.APIContext) { // DeleteOauthAuth api for deleting a authentication method func DeleteOauthAuth(ctx *context.APIContext) { - oauthIdString := ctx.PathParam("id") - oauthID, oauthIdErr := strconv.Atoi(oauthIdString) - if oauthIdErr != nil { - ctx.APIErrorInternal(oauthIdErr) + oauthIDString := ctx.PathParam("id") + oauthID, oauthIDErr := strconv.Atoi(oauthIDString) + if oauthIDErr != nil { + ctx.APIErrorInternal(oauthIDErr) } err := auth_model.DeleteSource(ctx, int64(oauthID)) - if err != nil { ctx.APIErrorInternal(err) return @@ -95,8 +99,6 @@ func SearchOauthAuth(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) authSources, maxResults, err := db.FindAndCount[auth_model.Source](ctx, auth_model.FindSourcesOptions{}) - // fmt.Printf("Count: %d, models: %v, err: %v", count, models[0].Name, err) - if err != nil { ctx.APIErrorInternal(err) return diff --git a/services/convert/auth_oauth.go b/services/convert/auth_oauth.go index a86c0fc9a7808..e76fbd1e3142a 100644 --- a/services/convert/auth_oauth.go +++ b/services/convert/auth_oauth.go @@ -17,7 +17,7 @@ func ToOauthProvider(ctx context.Context, provider *auth_model.Source) *api.Auth return nil } - return toOauthProvider(ctx, provider) + return toOauthProvider(provider) } // ToUsers convert list of user_model.User to list of api.User @@ -29,7 +29,7 @@ func ToOauthProviders(ctx context.Context, provider []*auth_model.Source) []*api return result } -func toOauthProvider(ctx context.Context, provider *auth_model.Source) *api.AuthOauth2Option { +func toOauthProvider(provider *auth_model.Source) *api.AuthOauth2Option { return &api.AuthOauth2Option{ ID: provider.ID, AuthenticationName: provider.Name, From c31df2562fa4ef4e22424315b6080530049479a7 Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Sun, 20 Apr 2025 04:12:11 +0200 Subject: [PATCH 08/13] Implement update patch method for api --- modules/structs/auth_oauth2.go | 3 -- routers/api/v1/admin/auth_oauth.go | 77 ++++++++++++++++++++++++++---- routers/api/v1/api.go | 3 +- 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/modules/structs/auth_oauth2.go b/modules/structs/auth_oauth2.go index 4ba1e46de5022..f7e10d7ef3a99 100644 --- a/modules/structs/auth_oauth2.go +++ b/modules/structs/auth_oauth2.go @@ -39,9 +39,6 @@ type CreateAuthOauth2Option struct { // EditUserOption edit user options type EditAuthOauth2Option struct { - // // required: true - SourceID int64 `json:"source_id"` - AuthenticationName string `json:"authentication_name" binding:"Required"` ProviderIconURL string `json:"provider_icon_url"` ProviderClientID string `json:"provider_client_id" binding:"Required"` diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index 8d03e5e961ec0..21a4215eb79a1 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -24,15 +24,6 @@ import ( func CreateOauthAuth(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.CreateAuthOauth2Option) - // ??? todo: what should I do here? - var scopes []string - // for _, s := range strings.Split(form.Oauth2Scopes, ",") { - // s = strings.TrimSpace(s) - // if s != "" { - // scopes = append(scopes, s) - // } - // } - discoveryURL, err := url.Parse(form.ProviderAutoDiscoveryURL) if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") { _ = fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", form.ProviderAutoDiscoveryURL) @@ -46,7 +37,7 @@ func CreateOauthAuth(ctx *context.APIContext) { OpenIDConnectAutoDiscoveryURL: form.ProviderAutoDiscoveryURL, CustomURLMapping: nil, IconURL: form.ProviderIconURL, - Scopes: scopes, + Scopes: generateScopes(), RequiredClaimName: form.RequiredClaimName, RequiredClaimValue: form.RequiredClaimValue, SkipLocalTwoFA: form.SkipLocal2FA, @@ -75,6 +66,47 @@ func CreateOauthAuth(ctx *context.APIContext) { // EditOauthAuth api for modifying a authentication method func EditOauthAuth(ctx *context.APIContext) { + oauthIDString := ctx.PathParam("id") + oauthID, oauthIDErr := strconv.Atoi(oauthIDString) + if oauthIDErr != nil { + ctx.APIErrorInternal(oauthIDErr) + } + + form := web.GetForm(ctx).(*api.CreateAuthOauth2Option) + + config := &oauth2.Source{ + Provider: "openidConnect", + ClientID: form.ProviderClientID, + ClientSecret: form.ProviderClientSecret, + OpenIDConnectAutoDiscoveryURL: form.ProviderAutoDiscoveryURL, + CustomURLMapping: nil, + IconURL: form.ProviderIconURL, + Scopes: generateScopes(), + RequiredClaimName: form.RequiredClaimName, + RequiredClaimValue: form.RequiredClaimValue, + SkipLocalTwoFA: form.SkipLocal2FA, + + GroupClaimName: form.ClaimNameProvidingGroupNameForSource, + RestrictedGroup: form.GroupClaimValueForRestrictedUsers, + AdminGroup: form.GroupClaimValueForAdministratorUsers, + GroupTeamMap: form.MapClaimedGroupsToOrganizationTeams, + GroupTeamMapRemoval: form.RemoveUsersFromSyncronizedTeams, + } + + updateErr := auth_model.UpdateSource(ctx, &auth_model.Source{ + ID: int64(oauthID), + Type: auth_model.OAuth2, + Name: form.AuthenticationName, + IsActive: true, + Cfg: config, + }) + + if updateErr != nil { + ctx.APIErrorInternal(updateErr) + return + } + + ctx.Status(http.StatusCreated) } // DeleteOauthAuth api for deleting a authentication method @@ -85,6 +117,17 @@ func DeleteOauthAuth(ctx *context.APIContext) { ctx.APIErrorInternal(oauthIDErr) } + source, sourceErr := auth_model.GetSourceByID(ctx, int64(oauthID)) + if sourceErr != nil { + ctx.APIErrorInternal(sourceErr) + return + } + + if source.Type != auth_model.OAuth2 { + ctx.APIErrorNotFound() + return + } + err := auth_model.DeleteSource(ctx, int64(oauthID)) if err != nil { ctx.APIErrorInternal(err) @@ -113,3 +156,17 @@ func SearchOauthAuth(ctx *context.APIContext) { ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, &results) } + +// ??? todo: what should I do here? +func generateScopes() []string { + var scopes []string + + // for _, s := range strings.Split(form.Oauth2Scopes, ",") { + // s = strings.TrimSpace(s) + // if s != "" { + // scopes = append(scopes, s) + // } + // } + + return scopes +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index e579aa6f549dc..9b29a91e90f89 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1660,7 +1660,8 @@ func Routes() *web.Router { m.Group("/identity-auth", func() { m.Group("/oauth", func() { m.Get("", admin.SearchOauthAuth) - m.Put("/new", bind(api.CreateAuthOauth2Option{}), admin.CreateOauthAuth) + m.Put("", bind(api.CreateAuthOauth2Option{}), admin.CreateOauthAuth) + m.Patch("/{id}", bind(api.EditAuthOauth2Option{}), admin.EditOauthAuth) m.Delete("/{id}", admin.DeleteOauthAuth) }) }) From 6a27fbeddab4e6b54fbe135a99b96e694aa701dd Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Sun, 20 Apr 2025 17:05:12 +0200 Subject: [PATCH 09/13] Add Swagger documentation for OAuth2 authentication endpoints --- routers/api/v1/admin/auth_oauth.go | 113 ++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index 21a4215eb79a1..bd7bc0b4fc227 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -22,6 +22,29 @@ import ( // CreateOauthAuth create a new external authentication for oauth2 func CreateOauthAuth(ctx *context.APIContext) { + // swagger:operation PUT /admin/identity-auth/oauth admin adminCreateOauth2Auth + // --- + // summary: Create an OAuth2 authentication source + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: body + // in: body + // required: true + // schema: + // "$ref": "#/definitions/CreateAuthOauth2Option" + // responses: + // "201": + // description: OAuth2 authentication source created successfully + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + // "422": + // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateAuthOauth2Option) discoveryURL, err := url.Parse(form.ProviderAutoDiscoveryURL) @@ -66,12 +89,54 @@ func CreateOauthAuth(ctx *context.APIContext) { // EditOauthAuth api for modifying a authentication method func EditOauthAuth(ctx *context.APIContext) { + // swagger:operation PATCH /admin/identity-auth/oauth/{id} admin adminEditOauth2Auth + // --- + // summary: Update an OAuth2 authentication source + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: id + // in: path + // description: authentication source ID + // type: integer + // format: int64 + // required: true + // - name: body + // in: body + // required: true + // schema: + // "$ref": "#/definitions/CreateAuthOauth2Option" + // responses: + // "201": + // description: OAuth2 authentication source updated successfully + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + // "422": + // "$ref": "#/responses/validationError" + oauthIDString := ctx.PathParam("id") oauthID, oauthIDErr := strconv.Atoi(oauthIDString) if oauthIDErr != nil { ctx.APIErrorInternal(oauthIDErr) } + source, sourceErr := auth_model.GetSourceByID(ctx, int64(oauthID)) + if sourceErr != nil { + ctx.APIErrorInternal(sourceErr) + return + } + + if source.Type != auth_model.OAuth2 { + ctx.APIErrorNotFound() + return + } + form := web.GetForm(ctx).(*api.CreateAuthOauth2Option) config := &oauth2.Source{ @@ -111,6 +176,28 @@ func EditOauthAuth(ctx *context.APIContext) { // DeleteOauthAuth api for deleting a authentication method func DeleteOauthAuth(ctx *context.APIContext) { + // swagger:operation DELETE /admin/identity-auth/oauth/{id} admin adminDeleteOauth2Auth + // --- + // summary: Delete an OAuth2 authentication source + // produces: + // - application/json + // parameters: + // - name: id + // in: path + // description: authentication source ID + // type: integer + // format: int64 + // required: true + // responses: + // "200": + // description: OAuth2 authentication source deleted successfully + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + // "422": + // "$ref": "#/responses/validationError" + oauthIDString := ctx.PathParam("id") oauthID, oauthIDErr := strconv.Atoi(oauthIDString) if oauthIDErr != nil { @@ -137,8 +224,32 @@ func DeleteOauthAuth(ctx *context.APIContext) { ctx.Status(http.StatusOK) } -// // SearchOauthAuth API for getting information of the configured authentication methods according the filter conditions +// SearchOauthAuth API for getting information of the configured authentication methods according the filter conditions func SearchOauthAuth(ctx *context.APIContext) { + // swagger:operation GET /admin/identity-auth/oauth admin adminSearchOauth2Auth + // --- + // summary: Search OAuth2 authentication sources + // produces: + // - application/json + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // description: "SearchResults of OAuth2 authentication sources" + // schema: + // type: array + // items: + // "$ref": "#/definitions/AuthOauth2Option" + // "403": + // "$ref": "#/responses/forbidden" + listOptions := utils.GetListOptions(ctx) authSources, maxResults, err := db.FindAndCount[auth_model.Source](ctx, auth_model.FindSourcesOptions{}) From 2de3030fb76c09a685e206aa41571cfa1c54cdfc Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Mon, 21 Apr 2025 05:25:32 +0200 Subject: [PATCH 10/13] Remove generateScopes function and set Scopes to an empty array in OAuth authentication --- routers/api/v1/admin/auth_oauth.go | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index bd7bc0b4fc227..924e2c723a92f 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -60,7 +60,7 @@ func CreateOauthAuth(ctx *context.APIContext) { OpenIDConnectAutoDiscoveryURL: form.ProviderAutoDiscoveryURL, CustomURLMapping: nil, IconURL: form.ProviderIconURL, - Scopes: generateScopes(), + Scopes: []string{}, RequiredClaimName: form.RequiredClaimName, RequiredClaimValue: form.RequiredClaimValue, SkipLocalTwoFA: form.SkipLocal2FA, @@ -146,7 +146,7 @@ func EditOauthAuth(ctx *context.APIContext) { OpenIDConnectAutoDiscoveryURL: form.ProviderAutoDiscoveryURL, CustomURLMapping: nil, IconURL: form.ProviderIconURL, - Scopes: generateScopes(), + Scopes: []string{}, RequiredClaimName: form.RequiredClaimName, RequiredClaimValue: form.RequiredClaimValue, SkipLocalTwoFA: form.SkipLocal2FA, @@ -267,17 +267,3 @@ func SearchOauthAuth(ctx *context.APIContext) { ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, &results) } - -// ??? todo: what should I do here? -func generateScopes() []string { - var scopes []string - - // for _, s := range strings.Split(form.Oauth2Scopes, ",") { - // s = strings.TrimSpace(s) - // if s != "" { - // scopes = append(scopes, s) - // } - // } - - return scopes -} From cb7b35945f7eae39a558c61c0cda0b303de840e6 Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Mon, 21 Apr 2025 17:58:29 +0200 Subject: [PATCH 11/13] Update copyright year to 2025 in OAuth2 related files --- modules/structs/auth_oauth2.go | 4 +--- routers/api/v1/admin/auth_oauth.go | 3 +-- services/convert/auth_oauth.go | 10 ++++------ 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/modules/structs/auth_oauth2.go b/modules/structs/auth_oauth2.go index f7e10d7ef3a99..7b5ed5d4efcbe 100644 --- a/modules/structs/auth_oauth2.go +++ b/modules/structs/auth_oauth2.go @@ -1,5 +1,4 @@ -// Copyright 2015 The Gogs Authors. All rights reserved. -// Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package structs @@ -7,7 +6,6 @@ package structs type AuthOauth2Option struct { ID int64 `json:"id"` AuthenticationName string `json:"authentication_name" binding:"Required"` - Type int `json:"type"` TypeName string `json:"type_name"` IsActive bool `json:"is_active"` diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index 924e2c723a92f..c594afa9cf8db 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -1,5 +1,4 @@ -// Copyright 2015 The Gogs Authors. All rights reserved. -// Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package admin diff --git a/services/convert/auth_oauth.go b/services/convert/auth_oauth.go index e76fbd1e3142a..93f90b0a520d0 100644 --- a/services/convert/auth_oauth.go +++ b/services/convert/auth_oauth.go @@ -1,5 +1,5 @@ -// // Copyright 2020 The Gitea Authors. All rights reserved. -// // SPDX-License-Identifier: MIT +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT package convert @@ -10,8 +10,7 @@ import ( api "code.gitea.io/gitea/modules/structs" ) -// ToUser convert user_model.User to api.User -// if doer is set, private information is added if the doer has the permission to see it +// ToOauthProvider convert auth_model.Source≤ to api.AuthOauth2Option func ToOauthProvider(ctx context.Context, provider *auth_model.Source) *api.AuthOauth2Option { if provider == nil { return nil @@ -20,7 +19,7 @@ func ToOauthProvider(ctx context.Context, provider *auth_model.Source) *api.Auth return toOauthProvider(provider) } -// ToUsers convert list of user_model.User to list of api.User +// ToOauthProviders convert list of auth_model.Source to list of api.AuthOauth2Option func ToOauthProviders(ctx context.Context, provider []*auth_model.Source) []*api.AuthOauth2Option { result := make([]*api.AuthOauth2Option, len(provider)) for i := range provider { @@ -33,7 +32,6 @@ func toOauthProvider(provider *auth_model.Source) *api.AuthOauth2Option { return &api.AuthOauth2Option{ ID: provider.ID, AuthenticationName: provider.Name, - Type: provider.Type.Int(), TypeName: provider.Type.String(), IsActive: provider.IsActive, From 098de090af830d844f267d0ced01ffe93345c44d Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Mon, 21 Apr 2025 18:03:48 +0200 Subject: [PATCH 12/13] Refactor OAuth authentication structures and endpoints to unify AuthSourceOption usage --- modules/structs/auth.go | 13 +++++++ modules/structs/auth_oauth2.go | 9 ----- routers/api/v1/admin/auth.go | 59 ++++++++++++++++++++++++++++++ routers/api/v1/admin/auth_oauth.go | 6 ++- services/convert/auth_oauth.go | 10 ++--- 5 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 modules/structs/auth.go create mode 100644 routers/api/v1/admin/auth.go diff --git a/modules/structs/auth.go b/modules/structs/auth.go new file mode 100644 index 0000000000000..2ee85a77070e3 --- /dev/null +++ b/modules/structs/auth.go @@ -0,0 +1,13 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package structs + +type AuthSourceOption struct { + ID int64 `json:"id"` + AuthenticationName string `json:"authentication_name" binding:"Required"` + TypeName string `json:"type_name"` + + IsActive bool `json:"is_active"` + IsSyncEnabled bool `json:"is_sync_enabled"` +} diff --git a/modules/structs/auth_oauth2.go b/modules/structs/auth_oauth2.go index 7b5ed5d4efcbe..b23533fadeb06 100644 --- a/modules/structs/auth_oauth2.go +++ b/modules/structs/auth_oauth2.go @@ -3,15 +3,6 @@ package structs -type AuthOauth2Option struct { - ID int64 `json:"id"` - AuthenticationName string `json:"authentication_name" binding:"Required"` - TypeName string `json:"type_name"` - - IsActive bool `json:"is_active"` - IsSyncEnabled bool `json:"is_sync_enabled"` -} - // CreateUserOption create user options type CreateAuthOauth2Option struct { AuthenticationName string `json:"authentication_name" binding:"Required"` diff --git a/routers/api/v1/admin/auth.go b/routers/api/v1/admin/auth.go new file mode 100644 index 0000000000000..2acfaadc75cc9 --- /dev/null +++ b/routers/api/v1/admin/auth.go @@ -0,0 +1,59 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package admin + +import ( + "net/http" + + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/convert" +) + +// SearchAuth API for getting information of the configured authentication methods according the filter conditions +func SearchAuth(ctx *context.APIContext) { + // swagger:operation GET /admin/identity-auth admin adminSearchAuth + // --- + // summary: Search authentication sources + // produces: + // - application/json + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // description: "SearchResults of authentication sources" + // schema: + // type: array + // items: + // "$ref": "#/definitions/AuthOauth2Option" + // "403": + // "$ref": "#/responses/forbidden" + + listOptions := utils.GetListOptions(ctx) + + authSources, maxResults, err := db.FindAndCount[auth_model.Source](ctx, auth_model.FindSourcesOptions{}) + if err != nil { + ctx.APIErrorInternal(err) + return + } + + results := make([]*api.AuthSourceOption, len(authSources)) + for i := range authSources { + results[i] = convert.ToOauthProvider(ctx, authSources[i]) + } + + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.SetTotalCountHeader(maxResults) + ctx.JSON(http.StatusOK, &results) +} diff --git a/routers/api/v1/admin/auth_oauth.go b/routers/api/v1/admin/auth_oauth.go index c594afa9cf8db..9bdf4b955665b 100644 --- a/routers/api/v1/admin/auth_oauth.go +++ b/routers/api/v1/admin/auth_oauth.go @@ -251,13 +251,15 @@ func SearchOauthAuth(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) - authSources, maxResults, err := db.FindAndCount[auth_model.Source](ctx, auth_model.FindSourcesOptions{}) + authSources, maxResults, err := db.FindAndCount[auth_model.Source](ctx, auth_model.FindSourcesOptions{ + LoginType: auth_model.OAuth2, + }) if err != nil { ctx.APIErrorInternal(err) return } - results := make([]*api.AuthOauth2Option, len(authSources)) + results := make([]*api.AuthSourceOption, len(authSources)) for i := range authSources { results[i] = convert.ToOauthProvider(ctx, authSources[i]) } diff --git a/services/convert/auth_oauth.go b/services/convert/auth_oauth.go index 93f90b0a520d0..c01b1bdeac7ca 100644 --- a/services/convert/auth_oauth.go +++ b/services/convert/auth_oauth.go @@ -11,7 +11,7 @@ import ( ) // ToOauthProvider convert auth_model.Source≤ to api.AuthOauth2Option -func ToOauthProvider(ctx context.Context, provider *auth_model.Source) *api.AuthOauth2Option { +func ToOauthProvider(ctx context.Context, provider *auth_model.Source) *api.AuthSourceOption { if provider == nil { return nil } @@ -20,16 +20,16 @@ func ToOauthProvider(ctx context.Context, provider *auth_model.Source) *api.Auth } // ToOauthProviders convert list of auth_model.Source to list of api.AuthOauth2Option -func ToOauthProviders(ctx context.Context, provider []*auth_model.Source) []*api.AuthOauth2Option { - result := make([]*api.AuthOauth2Option, len(provider)) +func ToOauthProviders(ctx context.Context, provider []*auth_model.Source) []*api.AuthSourceOption { + result := make([]*api.AuthSourceOption, len(provider)) for i := range provider { result[i] = ToOauthProvider(ctx, provider[i]) } return result } -func toOauthProvider(provider *auth_model.Source) *api.AuthOauth2Option { - return &api.AuthOauth2Option{ +func toOauthProvider(provider *auth_model.Source) *api.AuthSourceOption { + return &api.AuthSourceOption{ ID: provider.ID, AuthenticationName: provider.Name, TypeName: provider.Type.String(), From b58f94acd67968a024be7cd86e428de3576baf6a Mon Sep 17 00:00:00 2001 From: Tim Riedl Date: Fri, 25 Apr 2025 09:16:44 +0200 Subject: [PATCH 13/13] Add api route for searching identity authentication in admin panel --- routers/api/v1/api.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 9b9b079906242..6ac90165e2853 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1663,6 +1663,7 @@ func Routes() *web.Router { m.Group("/admin", func() { m.Group("/identity-auth", func() { + m.Get("", admin.SearchAuth) m.Group("/oauth", func() { m.Get("", admin.SearchOauthAuth) m.Put("", bind(api.CreateAuthOauth2Option{}), admin.CreateOauthAuth)