Skip to content

Commit

Permalink
Handle x_settings_action param for authorize intermediate action auth…
Browse files Browse the repository at this point in the history
  • Loading branch information
IniZio committed Feb 23, 2024
1 parent f675193 commit 0bf3a3b
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 34 deletions.
1 change: 1 addition & 0 deletions pkg/auth/handler/webapp/enter_login_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type EnterLoginIDHandler struct {
AuthenticationViewModel *viewmodels.AuthenticationViewModeler
Renderer Renderer
Identities EnterLoginIDService
OAuthClientResolver WebappOAuthClientResolver
}

func (h *EnterLoginIDHandler) GetData(userID string, r *http.Request, rw http.ResponseWriter) (map[string]interface{}, error) {
Expand Down
11 changes: 6 additions & 5 deletions pkg/auth/handler/webapp/settings_change_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ func ConfigureSettingsChangePasswordRoute(route httproute.Route) httproute.Route
}

type SettingsChangePasswordHandler struct {
ControllerFactory ControllerFactory
BaseViewModel *viewmodels.BaseViewModeler
Renderer Renderer
PasswordPolicy PasswordPolicy
ControllerFactory ControllerFactory
BaseViewModel *viewmodels.BaseViewModeler
Renderer Renderer
PasswordPolicy PasswordPolicy
OAuthClientResolver WebappOAuthClientResolver
}

func (h *SettingsChangePasswordHandler) GetData(r *http.Request, rw http.ResponseWriter) (map[string]interface{}, error) {
Expand Down Expand Up @@ -75,7 +76,7 @@ func (h *SettingsChangePasswordHandler) ServeHTTP(w http.ResponseWriter, r *http
ctrl.PostAction("", func() error {
userID := ctrl.RequireUserID()
opts := webapp.SessionOptions{
RedirectURI: webapp.DeriveSettingsRedirectURIFromRequest(r, "/settings"),
RedirectURI: webapp.DeriveSettingsRedirectURIFromRequest(r, h.OAuthClientResolver, "/settings"),
}
intent := intents.NewIntentChangePrimaryPassword(userID)
result, err := ctrl.EntryPointPost(opts, intent, func() (input interface{}, err error) {
Expand Down
11 changes: 6 additions & 5 deletions pkg/auth/handler/webapp/settings_change_secondary_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ func ConfigureSettingsChangeSecondaryPasswordRoute(route httproute.Route) httpro
}

type SettingsChangeSecondaryPasswordHandler struct {
ControllerFactory ControllerFactory
BaseViewModel *viewmodels.BaseViewModeler
Renderer Renderer
PasswordPolicy PasswordPolicy
ControllerFactory ControllerFactory
BaseViewModel *viewmodels.BaseViewModeler
Renderer Renderer
PasswordPolicy PasswordPolicy
OAuthClientResolver WebappOAuthClientResolver
}

func (h *SettingsChangeSecondaryPasswordHandler) GetData(r *http.Request, rw http.ResponseWriter) (map[string]interface{}, error) {
Expand Down Expand Up @@ -73,7 +74,7 @@ func (h *SettingsChangeSecondaryPasswordHandler) ServeHTTP(w http.ResponseWriter
ctrl.PostAction("", func() error {
userID := ctrl.RequireUserID()
opts := webapp.SessionOptions{
RedirectURI: webapp.DeriveSettingsRedirectURIFromRequest(r, "/settings"),
RedirectURI: webapp.DeriveSettingsRedirectURIFromRequest(r, h.OAuthClientResolver, "/settings"),
}
intent := intents.NewIntentChangeSecondaryPassword(userID)

Expand Down
3 changes: 2 additions & 1 deletion pkg/auth/handler/webapp/settings_profile_edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type SettingsProfileEditHandler struct {
StdAttrs SettingsProfileEditStdAttrsService
CustomAttrs SettingsProfileEditCustomAttrsService
ErrorCookie *webapp.ErrorCookie
OAuthClientResolver WebappOAuthClientResolver
}

func (h *SettingsProfileEditHandler) GetData(r *http.Request, rw http.ResponseWriter) (map[string]interface{}, error) {
Expand Down Expand Up @@ -116,7 +117,7 @@ func (h *SettingsProfileEditHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
}
}

result := webapp.Result{RedirectURI: webapp.DeriveSettingsRedirectURIFromRequest(r, "/settings/profile")}
result := webapp.Result{RedirectURI: webapp.DeriveSettingsRedirectURIFromRequest(r, h.OAuthClientResolver, "/settings/profile")}
result.WriteResponse(w, r)
return nil
})
Expand Down
33 changes: 19 additions & 14 deletions pkg/auth/webapp/redirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@ import (

"github.com/authgear/authgear-server/pkg/lib/config"
"github.com/authgear/authgear-server/pkg/util/httputil"
"github.com/iawaknahc/originmatcher"
)

var reservedRedirectURIs = []string{
"authgearsdk://host/path", // For Authgear SDK only, used for closing the webview
}

func GetRedirectURI(r *http.Request, trustProxy bool, defaultURI string) string {
redirectURI, err := httputil.GetRedirectURI(r, trustProxy)
if err != nil {
Expand All @@ -23,19 +20,29 @@ type OAuthClientResolver interface {
ResolveClient(clientID string) *config.OAuthClientConfig
}

func DeriveSettingsRedirectURIFromRequest(r *http.Request, defaultURI string) string {
func DeriveSettingsRedirectURIFromRequest(r *http.Request, clientResolver OAuthClientResolver, defaultURI string) string {
// 1. Redirect URL in query param (must be whitelisted)
// 2. Default redirect URL
// 3. `/settings`
redirectURIFromQuery := func() string {
clientID := r.URL.Query().Get("client_id")
redirectURI := r.URL.Query().Get("redirect_uri")
allowed := false
if clientID == "" {
return ""
}
client := clientResolver.ResolveClient(clientID)
if client == nil {
return ""
}

for _, u := range reservedRedirectURIs {
if u == redirectURI {
allowed = true
break
}
allowed := true
matcher, err := originmatcher.New(client.SettingsRedirectURIOrigins)
if err != nil {
return ""
}

if matcher.MatchOrigin(redirectURI) {
allowed = true
}

// 1. Redirect URL in query param (must be whitelisted)
Expand Down Expand Up @@ -75,10 +82,8 @@ func DerivePostLoginRedirectURIFromRequest(r *http.Request, clientResolver OAuth
return ""
}

allowedURIs := client.RedirectURIs
allowed := false

for _, u := range append(reservedRedirectURIs, allowedURIs...) {
for _, u := range client.RedirectURIs {
if u == redirectURI {
allowed = true
break
Expand Down
20 changes: 12 additions & 8 deletions pkg/auth/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/lib/config/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ var _ = Schema.Add("OAuthClientConfig", `
"client_uri": { "type": "string", "format": "uri" },
"client_name": { "type": "string", "minLength": 1 },
"name": { "type": "string" },
"x_settings_redirect_uri_origins": {
"type": "array",
"items": { "type": "string", "format": "uri" }
},
"x_application_type": { "type": "string", "enum": ["spa", "traditional_webapp", "native", "confidential", "third_party_app"] },
"x_max_concurrent_session": { "type": "integer", "enum": [0, 1] },
"redirect_uris": {
Expand Down Expand Up @@ -182,6 +186,7 @@ type OAuthClientConfig struct {
Name string `json:"name,omitempty"`
ApplicationType OAuthClientApplicationType `json:"x_application_type,omitempty"`
MaxConcurrentSession int `json:"x_max_concurrent_session,omitempty"`
SettingsRedirectURIOrigins []string `json:"x_settings_redirect_uri_origins,omitempty"`
RedirectURIs []string `json:"redirect_uris,omitempty"`
GrantTypes []string `json:"grant_types,omitempty"`
ResponseTypes []string `json:"response_types,omitempty"`
Expand Down
2 changes: 1 addition & 1 deletion pkg/lib/oauth/handler/handler_authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (h *AuthorizationHandler) Handle(r protocol.AuthorizationRequest) httputil.
Response: protocol.NewErrorResponse("unauthorized_client", "invalid client ID"),
}
}
redirectURI, errResp := parseRedirectURI(client, h.HTTPProto, h.HTTPOrigin, h.AppDomains, r)
redirectURI, errResp := parseAuthzRedirectURI(client, h.HTTPProto, h.HTTPOrigin, h.AppDomains, r)
if errResp != nil {
return authorizationResultError{
ResponseMode: r.ResponseMode(),
Expand Down
62 changes: 62 additions & 0 deletions pkg/lib/oauth/handler/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/authgear/authgear-server/pkg/lib/config"
"github.com/authgear/authgear-server/pkg/lib/oauth/protocol"
"github.com/authgear/authgear-server/pkg/util/httputil"
"github.com/iawaknahc/originmatcher"
)

type oauthRequest interface {
Expand Down Expand Up @@ -101,3 +102,64 @@ func validateRedirectURI(

return nil
}

func parseAuthzRedirectURI(
client *config.OAuthClientConfig,
httpProto httputil.HTTPProto,
httpOrigin httputil.HTTPOrigin,
domainWhitelist []string,
r protocol.AuthorizationRequest,
) (*url.URL, protocol.ErrorResponse) {
if r.SettingsAction() == "" {
return parseRedirectURI(client, httpProto, httpOrigin, domainWhitelist, r)
}

redirectURI, err := url.Parse(r.RedirectURI())
if err != nil {
return nil, protocol.NewErrorResponse("invalid_request", "invalid redirect URI")
}

err = validatePostSettingsRedirectURI(client, httpProto, httpOrigin, domainWhitelist, redirectURI)
if err != nil {
return nil, protocol.NewErrorResponse("invalid_request", err.Error())
}

switch r.SettingsAction() {
case "change_password":
settingsRedirectURI := &url.URL{Path: "/settings/change_password"}
queryItems := settingsRedirectURI.Query()
queryItems.Set("redirect_uri", r.RedirectURI())
queryItems.Set("client_id", r.ClientID())

settingsRedirectURI.RawQuery = queryItems.Encode()
return settingsRedirectURI, nil
default:
return nil, protocol.NewErrorResponse("invalid_request", "invalid settings action")
}
}

func validatePostSettingsRedirectURI(
client *config.OAuthClientConfig,
httpProto httputil.HTTPProto,
httpOrigin httputil.HTTPOrigin,
domainWhitelist []string,
redirectURI *url.URL,
) error {
redirectURIString := redirectURI.String()

matcher, err := originmatcher.New(client.SettingsRedirectURIOrigins)
if err != nil {
return err
}

if matcher.MatchOrigin(redirectURIString) {
return nil
}

err = validateRedirectURI(client, httpProto, httpOrigin, domainWhitelist, redirectURI)
if err != nil {
return err
}

return errors.New("redirect URI is not allowed")
}
1 change: 1 addition & 0 deletions pkg/lib/oauth/protocol/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func (r AuthorizationRequest) SSOEnabled() bool {
}
func (r AuthorizationRequest) ColorScheme() string { return r["x_color_scheme"] }
func (r AuthorizationRequest) OAuthProviderAlias() string { return r["x_oauth_provider_alias"] }
func (r AuthorizationRequest) SettingsAction() string { return r["x_settings_action"] }

type AuthorizationResponse map[string]string

Expand Down

0 comments on commit 0bf3a3b

Please sign in to comment.