mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 00:11:04 +00:00 
			
		
		
		
	fix: do 2FA on OpenID connect
This commit is contained in:
		
					parent
					
						
							
								9828aca733
							
						
					
				
			
			
				commit
				
					
						90e974cd24
					
				
			
		
					 6 changed files with 79 additions and 16 deletions
				
			
		| 
						 | 
				
			
			@ -40,8 +40,8 @@ func GetUserOpenIDs(ctx context.Context, uid int64) ([]*UserOpenID, error) {
 | 
			
		|||
	return openids, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isOpenIDUsed returns true if the openid has been used.
 | 
			
		||||
func isOpenIDUsed(ctx context.Context, uri string) (bool, error) {
 | 
			
		||||
// IsOpenIDUsed returns true if the openid has been used.
 | 
			
		||||
func IsOpenIDUsed(ctx context.Context, uri string) (bool, error) {
 | 
			
		||||
	if len(uri) == 0 {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ func (err ErrOpenIDAlreadyUsed) Unwrap() error {
 | 
			
		|||
// AddUserOpenID adds an pre-verified/normalized OpenID URI to given user.
 | 
			
		||||
// NOTE: make sure openid.URI is normalized already
 | 
			
		||||
func AddUserOpenID(ctx context.Context, openid *UserOpenID) error {
 | 
			
		||||
	used, err := isOpenIDUsed(ctx, openid.URI)
 | 
			
		||||
	used, err := IsOpenIDUsed(ctx, openid.URI)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else if used {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,6 +81,14 @@ func TwoFactorPost(ctx *context.Context) {
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Handle OpenID linking to user.
 | 
			
		||||
		if oid, ok := ctx.Session.Get("twofaOpenID").(string); ok {
 | 
			
		||||
			if err := user_model.AddUserOpenID(ctx, &user_model.UserOpenID{UID: u.ID, URI: oid}); err != nil {
 | 
			
		||||
				ctx.ServerError("AddUserOpenID", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		twofa.LastUsedPasscode = form.Passcode
 | 
			
		||||
		if err = auth.UpdateTwoFactor(ctx, twofa); err != nil {
 | 
			
		||||
			ctx.ServerError("UserSignIn", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -146,6 +154,14 @@ func TwoFactorScratchPost(ctx *context.Context) {
 | 
			
		|||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Handle OpenID linking to user.
 | 
			
		||||
		if oid, ok := ctx.Session.Get("twofaOpenID").(string); ok {
 | 
			
		||||
			if err := user_model.AddUserOpenID(ctx, &user_model.UserOpenID{UID: u.ID, URI: oid}); err != nil {
 | 
			
		||||
				ctx.ServerError("AddUserOpenID", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		handleSignInFull(ctx, u, remember, false)
 | 
			
		||||
		if ctx.Written() {
 | 
			
		||||
			return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -310,6 +310,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
 | 
			
		|||
		"openid_determined_username",
 | 
			
		||||
		"twofaUid",
 | 
			
		||||
		"twofaRemember",
 | 
			
		||||
		"twofaOpenID",
 | 
			
		||||
		"linkAccount",
 | 
			
		||||
	}, map[string]any{
 | 
			
		||||
		"uid": u.ID,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ import (
 | 
			
		|||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	auth_model "forgejo.org/models/auth"
 | 
			
		||||
	user_model "forgejo.org/models/user"
 | 
			
		||||
	"forgejo.org/modules/auth/openid"
 | 
			
		||||
	"forgejo.org/modules/base"
 | 
			
		||||
| 
						 | 
				
			
			@ -253,6 +254,7 @@ func ConnectOpenID(ctx *context.Context) {
 | 
			
		|||
// ConnectOpenIDPost handles submission of a form to connect an OpenID URI to an existing account
 | 
			
		||||
func ConnectOpenIDPost(ctx *context.Context) {
 | 
			
		||||
	form := web.GetForm(ctx).(*forms.ConnectOpenIDForm)
 | 
			
		||||
	remember, _ := ctx.Session.Get("openid_signin_remember").(bool)
 | 
			
		||||
	oid, _ := ctx.Session.Get("openid_verified_uri").(string)
 | 
			
		||||
	if oid == "" {
 | 
			
		||||
		ctx.Redirect(setting.AppSubURL + "/user/login/openid")
 | 
			
		||||
| 
						 | 
				
			
			@ -264,28 +266,63 @@ func ConnectOpenIDPost(ctx *context.Context) {
 | 
			
		|||
	ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
 | 
			
		||||
	ctx.Data["OpenID"] = oid
 | 
			
		||||
 | 
			
		||||
	u, _, err := auth.UserSignIn(ctx, form.UserName, form.Password)
 | 
			
		||||
	u, source, err := auth.UserSignIn(ctx, form.UserName, form.Password)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		handleSignInError(ctx, form.UserName, &form, tplConnectOID, "ConnectOpenIDPost", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// add OpenID for the user
 | 
			
		||||
	userOID := &user_model.UserOpenID{UID: u.ID, URI: oid}
 | 
			
		||||
	if err = user_model.AddUserOpenID(ctx, userOID); err != nil {
 | 
			
		||||
		if user_model.IsErrOpenIDAlreadyUsed(err) {
 | 
			
		||||
	// Check if OID is already in use.
 | 
			
		||||
	if used, err := user_model.IsOpenIDUsed(ctx, oid); err != nil {
 | 
			
		||||
		ctx.ServerError("IsOpenIDUsed", err)
 | 
			
		||||
		return
 | 
			
		||||
	} else if used {
 | 
			
		||||
		ctx.RenderWithErr(ctx.Tr("form.openid_been_used", oid), tplConnectOID, &form)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if 2FA needs to be done.
 | 
			
		||||
	has2FA, err := auth_model.HasTwoFactorByUID(ctx, u.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("HasTwoFactorByUID", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if skipper, ok := source.Cfg.(auth.LocalTwoFASkipper); !has2FA || (ok && skipper.IsSkipLocalTwoFA()) {
 | 
			
		||||
		// Link this OID to the user.
 | 
			
		||||
		if err := user_model.AddUserOpenID(ctx, &user_model.UserOpenID{UID: u.ID, URI: oid}); err != nil {
 | 
			
		||||
			ctx.ServerError("AddUserOpenID", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ctx.Flash.Success(ctx.Tr("settings.add_openid_success"))
 | 
			
		||||
 | 
			
		||||
	remember, _ := ctx.Session.Get("openid_signin_remember").(bool)
 | 
			
		||||
	log.Trace("Session stored openid-remember: %t", remember)
 | 
			
		||||
		handleSignIn(ctx, u, remember)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if the user has webauthn registration.
 | 
			
		||||
	hasWebAuthnTwofa, err := auth_model.HasWebAuthnRegistrationsByUID(ctx, u.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("HasWebAuthnRegistrationsByUID", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := updateSession(ctx, nil, map[string]any{
 | 
			
		||||
		"twofaUid":      u.ID,
 | 
			
		||||
		"twofaRemember": remember,
 | 
			
		||||
		"twofaOpenID":   oid,
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		ctx.ServerError("Unable to update session", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we have WebAuthn, redirect there first.
 | 
			
		||||
	if hasWebAuthnTwofa {
 | 
			
		||||
		ctx.Redirect(setting.AppSubURL + "/user/webauthn")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Fallback to TOTP.
 | 
			
		||||
	ctx.Redirect(setting.AppSubURL + "/user/two_factor")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterOpenID shows a form to create a new user authenticated via an OpenID URI
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -166,6 +166,14 @@ func WebAuthnLoginAssertionPost(ctx *context.Context) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle OpenID linking to user.
 | 
			
		||||
	if oid, ok := ctx.Session.Get("twofaOpenID").(string); ok {
 | 
			
		||||
		if err := user_model.AddUserOpenID(ctx, &user_model.UserOpenID{UID: user.ID, URI: oid}); err != nil {
 | 
			
		||||
			ctx.ServerError("AddUserOpenID", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	remember := ctx.Session.Get("twofaRemember").(bool)
 | 
			
		||||
	redirect := handleSignInFull(ctx, user, remember, false)
 | 
			
		||||
	if redirect == "" {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,6 +77,7 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore
 | 
			
		|||
	_ = sess.Delete("openid_determined_username")
 | 
			
		||||
	_ = sess.Delete("twofaUid")
 | 
			
		||||
	_ = sess.Delete("twofaRemember")
 | 
			
		||||
	_ = sess.Delete("twofaOpenID")
 | 
			
		||||
	_ = sess.Delete("webauthnAssertion")
 | 
			
		||||
	_ = sess.Delete("linkAccount")
 | 
			
		||||
	err = sess.Set("uid", user.ID)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue