mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-02-04 17:24:48 +00:00
feat: auto detect callback url (#583)
Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
@@ -70,6 +70,13 @@ type OidcInvalidAuthorizationCodeError struct{}
|
||||
func (e *OidcInvalidAuthorizationCodeError) Error() string { return "invalid authorization code" }
|
||||
func (e *OidcInvalidAuthorizationCodeError) HttpStatusCode() int { return 400 }
|
||||
|
||||
type OidcMissingCallbackURLError struct{}
|
||||
|
||||
func (e *OidcMissingCallbackURLError) Error() string {
|
||||
return "unable to detect callback url, it might be necessary for an admin to fix this"
|
||||
}
|
||||
func (e *OidcMissingCallbackURLError) HttpStatusCode() int { return 400 }
|
||||
|
||||
type OidcInvalidCallbackURLError struct{}
|
||||
|
||||
func (e *OidcInvalidCallbackURLError) Error() string {
|
||||
|
||||
@@ -26,7 +26,7 @@ type OidcClientWithAllowedGroupsCountDto struct {
|
||||
|
||||
type OidcClientCreateDto struct {
|
||||
Name string `json:"name" binding:"required,max=50"`
|
||||
CallbackURLs []string `json:"callbackURLs" binding:"required"`
|
||||
CallbackURLs []string `json:"callbackURLs"`
|
||||
LogoutCallbackURLs []string `json:"logoutCallbackURLs"`
|
||||
IsPublic bool `json:"isPublic"`
|
||||
PkceEnabled bool `json:"pkceEnabled"`
|
||||
|
||||
@@ -73,7 +73,7 @@ func (s *OidcService) Authorize(ctx context.Context, input dto.AuthorizeOidcClie
|
||||
}
|
||||
|
||||
// Get the callback URL of the client. Return an error if the provided callback URL is not allowed
|
||||
callbackURL, err := s.getCallbackURL(client.CallbackURLs, input.CallbackURL)
|
||||
callbackURL, err := s.getCallbackURL(client.CallbackURLs, input.CallbackURL, input.ClientID, tx, ctx)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@@ -947,7 +947,7 @@ func (s *OidcService) ValidateEndSession(ctx context.Context, input dto.OidcLogo
|
||||
return "", &common.OidcNoCallbackURLError{}
|
||||
}
|
||||
|
||||
callbackURL, err := s.getCallbackURL(userAuthorizedOIDCClient.Client.LogoutCallbackURLs, input.PostLogoutRedirectUri)
|
||||
callbackURL, err := s.getCallbackURL(userAuthorizedOIDCClient.Client.LogoutCallbackURLs, input.PostLogoutRedirectUri, userAuthorizedOIDCClient.Client.ID, s.db, ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -1006,23 +1006,55 @@ func (s *OidcService) validateCodeVerifier(codeVerifier, codeChallenge string, c
|
||||
return encodedVerifierHash == codeChallenge
|
||||
}
|
||||
|
||||
func (s *OidcService) getCallbackURL(urls []string, inputCallbackURL string) (callbackURL string, err error) {
|
||||
func (s *OidcService) getCallbackURL(urls []string, inputCallbackURL string, clientID string, tx *gorm.DB, ctx context.Context) (callbackURL string, err error) {
|
||||
// If no input callback URL provided, use the first configured URL
|
||||
if inputCallbackURL == "" {
|
||||
return urls[0], nil
|
||||
if len(urls) > 0 {
|
||||
return urls[0], nil
|
||||
}
|
||||
// If no URLs are configured and no input URL, this is an error
|
||||
return "", &common.OidcMissingCallbackURLError{}
|
||||
}
|
||||
|
||||
for _, callbackPattern := range urls {
|
||||
regexPattern := "^" + strings.ReplaceAll(regexp.QuoteMeta(callbackPattern), `\*`, ".*") + "$"
|
||||
matched, err := regexp.MatchString(regexPattern, inputCallbackURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if matched {
|
||||
return inputCallbackURL, nil
|
||||
// If URLs are already configured, validate against them
|
||||
if len(urls) > 0 {
|
||||
for _, callbackPattern := range urls {
|
||||
regexPattern := "^" + strings.ReplaceAll(regexp.QuoteMeta(callbackPattern), `\*`, ".*") + "$"
|
||||
matched, err := regexp.MatchString(regexPattern, inputCallbackURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if matched {
|
||||
return inputCallbackURL, nil
|
||||
}
|
||||
}
|
||||
return "", &common.OidcInvalidCallbackURLError{}
|
||||
}
|
||||
|
||||
return "", &common.OidcInvalidCallbackURLError{}
|
||||
// If no URLs are configured, trust and store the first URL (TOFU)
|
||||
err = s.addCallbackURLToClient(ctx, clientID, inputCallbackURL, tx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return inputCallbackURL, nil
|
||||
}
|
||||
|
||||
func (s *OidcService) addCallbackURLToClient(ctx context.Context, clientID string, callbackURL string, tx *gorm.DB) error {
|
||||
var client model.OidcClient
|
||||
err := tx.WithContext(ctx).First(&client, "id = ?", clientID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add the new callback URL to the existing list
|
||||
client.CallbackURLs = append(client.CallbackURLs, callbackURL)
|
||||
|
||||
err = tx.WithContext(ctx).Save(&client).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *OidcService) CreateDeviceAuthorization(ctx context.Context, input dto.OidcDeviceAuthorizationRequestDto) (*dto.OidcDeviceAuthorizationResponseDto, error) {
|
||||
|
||||
Reference in New Issue
Block a user