mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Add ability to set multiple redirect URIs in OAuth application UI (#25072)
OAuth applications can already have multiple redirect URIs if created/edited over API. This change allows for setting multiple redirect URIs through the UI as a comma-separated list (e. g. `https://example.org/redirect,https://redirect.example.org`) <details> <summary>Screenshots</summary>   </details> Closes #25068
This commit is contained in:
		@@ -51,14 +51,6 @@ func (app *OAuth2Application) TableName() string {
 | 
				
			|||||||
	return "oauth2_application"
 | 
						return "oauth2_application"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PrimaryRedirectURI returns the first redirect uri or an empty string if empty
 | 
					 | 
				
			||||||
func (app *OAuth2Application) PrimaryRedirectURI() string {
 | 
					 | 
				
			||||||
	if len(app.RedirectURIs) == 0 {
 | 
					 | 
				
			||||||
		return ""
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return app.RedirectURIs[0]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ContainsRedirectURI checks if redirectURI is allowed for app
 | 
					// ContainsRedirectURI checks if redirectURI is allowed for app
 | 
				
			||||||
func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
 | 
					func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
 | 
				
			||||||
	if !app.ConfidentialClient {
 | 
						if !app.ConfidentialClient {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package util
 | 
					package util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "unicode/utf8"
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// in UTF8 "…" is 3 bytes so doesn't really gain us anything...
 | 
					// in UTF8 "…" is 3 bytes so doesn't really gain us anything...
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -35,3 +38,17 @@ func SplitStringAtByteN(input string, n int) (left, right string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return input[:end] + utf8Ellipsis, utf8Ellipsis + input[end:]
 | 
						return input[:end] + utf8Ellipsis, utf8Ellipsis + input[end:]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SplitTrimSpace splits the string at given separator and trims leading and trailing space
 | 
				
			||||||
 | 
					func SplitTrimSpace(input, sep string) []string {
 | 
				
			||||||
 | 
						// replace CRLF with LF
 | 
				
			||||||
 | 
						input = strings.ReplaceAll(input, "\r\n", "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var stringList []string
 | 
				
			||||||
 | 
						for _, s := range strings.Split(input, sep) {
 | 
				
			||||||
 | 
							// trim leading and trailing space
 | 
				
			||||||
 | 
							stringList = append(stringList, strings.TrimSpace(s))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return stringList
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -826,7 +826,7 @@ create_oauth2_application_success = You've successfully created a new OAuth2 app
 | 
				
			|||||||
update_oauth2_application_success = You've successfully updated the OAuth2 application.
 | 
					update_oauth2_application_success = You've successfully updated the OAuth2 application.
 | 
				
			||||||
oauth2_application_name = Application Name
 | 
					oauth2_application_name = Application Name
 | 
				
			||||||
oauth2_confidential_client = Confidential Client. Select for apps that keep the secret confidential, such as web apps. Do not select for native apps including desktop and mobile apps.
 | 
					oauth2_confidential_client = Confidential Client. Select for apps that keep the secret confidential, such as web apps. Do not select for native apps including desktop and mobile apps.
 | 
				
			||||||
oauth2_redirect_uri = Redirect URI
 | 
					oauth2_redirect_uris = Redirect URIs. Please use a new line for every URI.
 | 
				
			||||||
save_application = Save
 | 
					save_application = Save
 | 
				
			||||||
oauth2_client_id = Client ID
 | 
					oauth2_client_id = Client ID
 | 
				
			||||||
oauth2_client_secret = Client Secret
 | 
					oauth2_client_secret = Client Secret
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models/auth"
 | 
						"code.gitea.io/gitea/models/auth"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web"
 | 
						"code.gitea.io/gitea/modules/web"
 | 
				
			||||||
	"code.gitea.io/gitea/services/forms"
 | 
						"code.gitea.io/gitea/services/forms"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -40,7 +41,7 @@ func (oa *OAuth2CommonHandlers) AddApp(ctx *context.Context) {
 | 
				
			|||||||
	// TODO validate redirect URI
 | 
						// TODO validate redirect URI
 | 
				
			||||||
	app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
 | 
						app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
 | 
				
			||||||
		Name:               form.Name,
 | 
							Name:               form.Name,
 | 
				
			||||||
		RedirectURIs:       []string{form.RedirectURI},
 | 
							RedirectURIs:       util.SplitTrimSpace(form.RedirectURIs, "\n"),
 | 
				
			||||||
		UserID:             oa.OwnerID,
 | 
							UserID:             oa.OwnerID,
 | 
				
			||||||
		ConfidentialClient: form.ConfidentialClient,
 | 
							ConfidentialClient: form.ConfidentialClient,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
@@ -93,7 +94,7 @@ func (oa *OAuth2CommonHandlers) EditSave(ctx *context.Context) {
 | 
				
			|||||||
	if ctx.Data["App"], err = auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{
 | 
						if ctx.Data["App"], err = auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{
 | 
				
			||||||
		ID:                 ctx.ParamsInt64("id"),
 | 
							ID:                 ctx.ParamsInt64("id"),
 | 
				
			||||||
		Name:               form.Name,
 | 
							Name:               form.Name,
 | 
				
			||||||
		RedirectURIs:       []string{form.RedirectURI},
 | 
							RedirectURIs:       util.SplitTrimSpace(form.RedirectURIs, "\n"),
 | 
				
			||||||
		UserID:             oa.OwnerID,
 | 
							UserID:             oa.OwnerID,
 | 
				
			||||||
		ConfidentialClient: form.ConfidentialClient,
 | 
							ConfidentialClient: form.ConfidentialClient,
 | 
				
			||||||
	}); err != nil {
 | 
						}); err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -398,7 +398,7 @@ func (f *NewAccessTokenForm) GetScope() (auth_model.AccessTokenScope, error) {
 | 
				
			|||||||
// EditOAuth2ApplicationForm form for editing oauth2 applications
 | 
					// EditOAuth2ApplicationForm form for editing oauth2 applications
 | 
				
			||||||
type EditOAuth2ApplicationForm struct {
 | 
					type EditOAuth2ApplicationForm struct {
 | 
				
			||||||
	Name               string `binding:"Required;MaxSize(255)" form:"application_name"`
 | 
						Name               string `binding:"Required;MaxSize(255)" form:"application_name"`
 | 
				
			||||||
	RedirectURI        string `binding:"Required" form:"redirect_uri"`
 | 
						RedirectURIs       string `binding:"Required" form:"redirect_uris"`
 | 
				
			||||||
	ConfidentialClient bool   `form:"confidential_client"`
 | 
						ConfidentialClient bool   `form:"confidential_client"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,8 +39,8 @@
 | 
				
			|||||||
				<input id="application-name" value="{{.App.Name}}" name="application_name" required maxlength="255">
 | 
									<input id="application-name" value="{{.App.Name}}" name="application_name" required maxlength="255">
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<div class="field {{if .Err_RedirectURI}}error{{end}}">
 | 
								<div class="field {{if .Err_RedirectURI}}error{{end}}">
 | 
				
			||||||
				<label for="redirect-uri">{{.locale.Tr "settings.oauth2_redirect_uri"}}</label>
 | 
									<label for="redirect-uris">{{.locale.Tr "settings.oauth2_redirect_uris"}}</label>
 | 
				
			||||||
				<input type="url" name="redirect_uri" value="{{.App.PrimaryRedirectURI}}" id="redirect-uri" required>
 | 
									<textarea name="redirect_uris" id="redirect-uris" required>{{StringUtils.Join .App.RedirectURIs "\n"}}</textarea>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<div class="field ui checkbox {{if .Err_ConfidentialClient}}error{{end}}">
 | 
								<div class="field ui checkbox {{if .Err_ConfidentialClient}}error{{end}}">
 | 
				
			||||||
				<label>{{.locale.Tr "settings.oauth2_confidential_client"}}</label>
 | 
									<label>{{.locale.Tr "settings.oauth2_confidential_client"}}</label>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,8 +34,8 @@
 | 
				
			|||||||
			<input id="application-name" name="application_name" value="{{.application_name}}" required maxlength="255">
 | 
								<input id="application-name" name="application_name" value="{{.application_name}}" required maxlength="255">
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div class="field {{if .Err_RedirectURI}}error{{end}}">
 | 
							<div class="field {{if .Err_RedirectURI}}error{{end}}">
 | 
				
			||||||
			<label for="redirect-uri">{{.locale.Tr "settings.oauth2_redirect_uri"}}</label>
 | 
								<label for="redirect-uris">{{.locale.Tr "settings.oauth2_redirect_uris"}}</label>
 | 
				
			||||||
			<input type="url" name="redirect_uri" id="redirect-uri">
 | 
								<textarea name="redirect_uris" id="redirect-uris"></textarea>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div class="field ui checkbox {{if .Err_ConfidentialClient}}error{{end}}">
 | 
							<div class="field ui checkbox {{if .Err_ConfidentialClient}}error{{end}}">
 | 
				
			||||||
			<label>{{.locale.Tr "settings.oauth2_confidential_client"}}</label>
 | 
								<label>{{.locale.Tr "settings.oauth2_confidential_client"}}</label>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user