mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-09-12 22:07:17 +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
|
return openids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isOpenIDUsed returns true if the openid has been used.
|
// IsOpenIDUsed returns true if the openid has been used.
|
||||||
func isOpenIDUsed(ctx context.Context, uri string) (bool, error) {
|
func IsOpenIDUsed(ctx context.Context, uri string) (bool, error) {
|
||||||
if len(uri) == 0 {
|
if len(uri) == 0 {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ func (err ErrOpenIDAlreadyUsed) Unwrap() error {
|
||||||
// AddUserOpenID adds an pre-verified/normalized OpenID URI to given user.
|
// AddUserOpenID adds an pre-verified/normalized OpenID URI to given user.
|
||||||
// NOTE: make sure openid.URI is normalized already
|
// NOTE: make sure openid.URI is normalized already
|
||||||
func AddUserOpenID(ctx context.Context, openid *UserOpenID) error {
|
func AddUserOpenID(ctx context.Context, openid *UserOpenID) error {
|
||||||
used, err := isOpenIDUsed(ctx, openid.URI)
|
used, err := IsOpenIDUsed(ctx, openid.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if used {
|
} 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
|
twofa.LastUsedPasscode = form.Passcode
|
||||||
if err = auth.UpdateTwoFactor(ctx, twofa); err != nil {
|
if err = auth.UpdateTwoFactor(ctx, twofa); err != nil {
|
||||||
ctx.ServerError("UserSignIn", err)
|
ctx.ServerError("UserSignIn", err)
|
||||||
|
@ -146,6 +154,14 @@ func TwoFactorScratchPost(ctx *context.Context) {
|
||||||
return
|
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)
|
handleSignInFull(ctx, u, remember, false)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
|
|
|
@ -310,6 +310,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||||
"openid_determined_username",
|
"openid_determined_username",
|
||||||
"twofaUid",
|
"twofaUid",
|
||||||
"twofaRemember",
|
"twofaRemember",
|
||||||
|
"twofaOpenID",
|
||||||
"linkAccount",
|
"linkAccount",
|
||||||
}, map[string]any{
|
}, map[string]any{
|
||||||
"uid": u.ID,
|
"uid": u.ID,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
auth_model "forgejo.org/models/auth"
|
||||||
user_model "forgejo.org/models/user"
|
user_model "forgejo.org/models/user"
|
||||||
"forgejo.org/modules/auth/openid"
|
"forgejo.org/modules/auth/openid"
|
||||||
"forgejo.org/modules/base"
|
"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
|
// ConnectOpenIDPost handles submission of a form to connect an OpenID URI to an existing account
|
||||||
func ConnectOpenIDPost(ctx *context.Context) {
|
func ConnectOpenIDPost(ctx *context.Context) {
|
||||||
form := web.GetForm(ctx).(*forms.ConnectOpenIDForm)
|
form := web.GetForm(ctx).(*forms.ConnectOpenIDForm)
|
||||||
|
remember, _ := ctx.Session.Get("openid_signin_remember").(bool)
|
||||||
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
||||||
if oid == "" {
|
if oid == "" {
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
||||||
|
@ -264,28 +266,63 @@ func ConnectOpenIDPost(ctx *context.Context) {
|
||||||
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
|
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
|
||||||
ctx.Data["OpenID"] = oid
|
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 {
|
if err != nil {
|
||||||
handleSignInError(ctx, form.UserName, &form, tplConnectOID, "ConnectOpenIDPost", err)
|
handleSignInError(ctx, form.UserName, &form, tplConnectOID, "ConnectOpenIDPost", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// add OpenID for the user
|
// Check if OID is already in use.
|
||||||
userOID := &user_model.UserOpenID{UID: u.ID, URI: oid}
|
if used, err := user_model.IsOpenIDUsed(ctx, oid); err != nil {
|
||||||
if err = user_model.AddUserOpenID(ctx, userOID); err != nil {
|
ctx.ServerError("IsOpenIDUsed", err)
|
||||||
if user_model.IsErrOpenIDAlreadyUsed(err) {
|
return
|
||||||
ctx.RenderWithErr(ctx.Tr("form.openid_been_used", oid), tplConnectOID, &form)
|
} else if used {
|
||||||
return
|
ctx.RenderWithErr(ctx.Tr("form.openid_been_used", oid), tplConnectOID, &form)
|
||||||
}
|
|
||||||
ctx.ServerError("AddUserOpenID", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Flash.Success(ctx.Tr("settings.add_openid_success"))
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
remember, _ := ctx.Session.Get("openid_signin_remember").(bool)
|
ctx.Flash.Success(ctx.Tr("settings.add_openid_success"))
|
||||||
log.Trace("Session stored openid-remember: %t", remember)
|
handleSignIn(ctx, u, 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
|
// 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)
|
remember := ctx.Session.Get("twofaRemember").(bool)
|
||||||
redirect := handleSignInFull(ctx, user, remember, false)
|
redirect := handleSignInFull(ctx, user, remember, false)
|
||||||
if redirect == "" {
|
if redirect == "" {
|
||||||
|
|
|
@ -77,6 +77,7 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore
|
||||||
_ = sess.Delete("openid_determined_username")
|
_ = sess.Delete("openid_determined_username")
|
||||||
_ = sess.Delete("twofaUid")
|
_ = sess.Delete("twofaUid")
|
||||||
_ = sess.Delete("twofaRemember")
|
_ = sess.Delete("twofaRemember")
|
||||||
|
_ = sess.Delete("twofaOpenID")
|
||||||
_ = sess.Delete("webauthnAssertion")
|
_ = sess.Delete("webauthnAssertion")
|
||||||
_ = sess.Delete("linkAccount")
|
_ = sess.Delete("linkAccount")
|
||||||
err = sess.Set("uid", user.ID)
|
err = sess.Set("uid", user.ID)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue