feat: publish v1.0.0-rc.36-custom.1
Publish Docker image (Multi-arch) / Build & push (amd64) (push) Failing after 9m20s
Release (Linux, macOS, Windows) / Linux Release (push) Failing after 55m7s
Publish Docker image (Multi-arch) / Build & push (arm64) (push) Canceled after 0s
Release (Linux, macOS, Windows) / macOS Release (push) Canceled after 0s
Release (Linux, macOS, Windows) / Windows Release (push) Canceled after 0s
Publish Docker image (Multi-arch) / Create multi-arch manifests (push) Canceled after 0s
Publish Docker image (Multi-arch) / Build & push (amd64) (push) Failing after 9m20s
Release (Linux, macOS, Windows) / Linux Release (push) Failing after 55m7s
Publish Docker image (Multi-arch) / Build & push (arm64) (push) Canceled after 0s
Release (Linux, macOS, Windows) / macOS Release (push) Canceled after 0s
Release (Linux, macOS, Windows) / Windows Release (push) Canceled after 0s
Publish Docker image (Multi-arch) / Create multi-arch manifests (push) Canceled after 0s
This commit is contained in:
commit
bb7968b612
2381 files changed
+551494
No files matched your search
@@ -0,0 +1,149 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
)
|
||||
|
||||
const LoginVerificationTTL = 5 * time.Minute
|
||||
|
||||
type LoginChallenge struct {
|
||||
RequireVerification bool `json:"require_verification"`
|
||||
FlowToken string `json:"flow_token"`
|
||||
ExpiresAt int64 `json:"expires_at"`
|
||||
Methods []VerificationMethodOption `json:"methods"`
|
||||
}
|
||||
|
||||
type loginFlowPayload struct {
|
||||
AuthVersion int64 `json:"auth_version"`
|
||||
LoginMethod string `json:"login_method"`
|
||||
}
|
||||
|
||||
// LoginVerification is server-owned state read from a primary-authenticated flow.
|
||||
// It is never constructed from a user ID or an authentication claim in a request.
|
||||
type LoginVerification struct {
|
||||
Flow *model.AuthFlow
|
||||
State *model.UserVerificationState
|
||||
payload loginFlowPayload
|
||||
}
|
||||
|
||||
func StartLoginVerification(user *model.User, loginMethod string) (*LoginChallenge, error) {
|
||||
if user == nil || user.Id <= 0 || user.AuthVersion <= 0 || loginMethod == "" {
|
||||
return nil, model.ErrAuthFlowInvalid
|
||||
}
|
||||
state, err := model.GetUserVerificationState(user.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if state.Status != common.UserStatusEnabled || state.AuthVersion != user.AuthVersion {
|
||||
return nil, model.ErrUserSessionInactive
|
||||
}
|
||||
methods, err := securityVerificationPolicy(VerificationScopeLogin, *state)
|
||||
if err != nil || len(methods) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
available := false
|
||||
for _, method := range methods {
|
||||
available = available || method.Available
|
||||
}
|
||||
if !available {
|
||||
return nil, ErrVerificationUnavailable
|
||||
}
|
||||
payload, err := common.Marshal(loginFlowPayload{AuthVersion: state.AuthVersion, LoginMethod: loginMethod})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expiresAt := time.Now().Add(LoginVerificationTTL)
|
||||
token, _, err := model.CreateAuthFlow(model.AuthFlowCreate{
|
||||
Purpose: model.AuthFlowPurposeLoginVerification, UserId: user.Id,
|
||||
Payload: string(payload), ExpiresAt: expiresAt,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LoginChallenge{RequireVerification: true, FlowToken: token, ExpiresAt: expiresAt.Unix(), Methods: methods}, nil
|
||||
}
|
||||
|
||||
func RequireLoginVerification(token, method string) (*LoginVerification, error) {
|
||||
flow, err := model.GetAuthFlow(token, model.AuthFlowMatch{Purpose: model.AuthFlowPurposeLoginVerification})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var payload loginFlowPayload
|
||||
if err := common.UnmarshalJsonStr(flow.Payload, &payload); err != nil || payload.AuthVersion <= 0 || payload.LoginMethod == "" {
|
||||
return nil, model.ErrAuthFlowInvalid
|
||||
}
|
||||
state, err := model.GetUserVerificationState(flow.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if state.Status != common.UserStatusEnabled || state.AuthVersion != payload.AuthVersion {
|
||||
return nil, model.ErrUserSessionInactive
|
||||
}
|
||||
if err := requireLoginVerificationMethod(state, method); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LoginVerification{Flow: flow, State: state, payload: payload}, nil
|
||||
}
|
||||
|
||||
func requireLoginVerificationMethod(state *model.UserVerificationState, method string) error {
|
||||
methods, err := securityVerificationPolicy(VerificationScopeLogin, *state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, option := range methods {
|
||||
if option.Method != method {
|
||||
continue
|
||||
}
|
||||
if !option.Available {
|
||||
return ErrVerificationUnavailable
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return ErrProofMethod
|
||||
}
|
||||
|
||||
func VerifyLoginCode(token, code, ip, userAgent string) (*AuthBundle, error) {
|
||||
verification, err := RequireLoginVerification(token, VerificationMethodTwoFA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
twoFA, err := model.GetTwoFAByUserId(verification.State.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := VerifyTwoFactorCode(twoFA, code); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return CompleteLoginVerification(token, verification, VerificationMethodTwoFA, ip, userAgent)
|
||||
}
|
||||
|
||||
// CompleteLoginVerification must only run after a concrete factor ceremony.
|
||||
// Recheck the bound version and method while consuming the flow and creating the
|
||||
// session atomically; a different request cannot reuse this authorization.
|
||||
func CompleteLoginVerification(token string, verification *LoginVerification, method, ip, userAgent string) (*AuthBundle, error) {
|
||||
if verification == nil || verification.Flow == nil || verification.State == nil {
|
||||
return nil, model.ErrAuthFlowInvalid
|
||||
}
|
||||
session, refreshSecret, err := newLoginSession(verification.State.UserID, verification.payload.AuthVersion, verification.payload.LoginMethod, ip, userAgent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := model.CreateUserSessionFromLoginFlow(token, session, func(flow *model.AuthFlow, state *model.UserVerificationState) error {
|
||||
var payload loginFlowPayload
|
||||
if flow.Id != verification.Flow.Id || common.UnmarshalJsonStr(flow.Payload, &payload) != nil || payload != verification.payload {
|
||||
return model.ErrAuthFlowInvalid
|
||||
}
|
||||
return requireLoginVerificationMethod(state, method)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bundle, err := issueAuthBundle(session, session.SID+"."+refreshSecret, true)
|
||||
if err != nil {
|
||||
_, _ = model.RevokeUserSession(session.UserID, session.SID, "token_issue_failed")
|
||||
return nil, err
|
||||
}
|
||||
return bundle, nil
|
||||
}
|
||||
Reference in new issue
Block a user