package web import ( "errors" "log/slog" "net/http" "tug/internal/transport/web/text" ) const GoogleLoginPath = "/login/google" func GoogleEntry() LoginEntry { return LoginEntry{Label: text.LoginGoogle, Action: GoogleLoginPath} } var ( ErrLoginUnlinked = errors.New("web: no account is linked to this identity") ErrLoginRefused = errors.New("web: this identity cannot sign in") ErrLoginCancelled = errors.New("web: the sign-in was cancelled at the provider") ErrLoginUnreachable = errors.New("web: the provider could not be reached or did not verify") ErrStepExpired = errors.New("web: the round trip expired or was already used") ErrFormExpired = errors.New("web: the round trip came from another page") ) func (k *Kit) FinishLogin(w http.ResponseWriter, r *http.Request, state, target string, err error, entries ...LoginEntry) { if verr := validateEntries(entries); verr != nil { slog.Error("login outcome refused", "err", verr) k.renderError(w, r, http.StatusInternalServerError, text.ErrorInternalTitle, text.ErrorInternal) return } state = echoed(state, echoStateBytes) l := &login{kit: k, entries: entries} form := func(status int, message text.Key) { l.render(w, r, status, loginData{State: state, Message: message}, "") } switch { case err == nil: k.RotateCSRF(w) k.Continue(w, r, target) case errors.Is(err, ErrStepExpired): slog.Info("external sign-in round trip refused", "err", err) k.renderError(w, r, http.StatusBadRequest, text.ErrorBadRequestTitle, text.ContinuationInvalid) case errors.Is(err, ErrFormExpired): slog.Warn("external sign-in round trip refused", "err", err) k.renderError(w, r, http.StatusForbidden, text.ErrorForbiddenTitle, text.ErrorFormExpired) case errors.Is(err, ErrLoginExpired): k.renderError(w, r, http.StatusBadRequest, text.ErrorBadRequestTitle, text.LoginExpired) case errors.Is(err, ErrLoginElsewhere): k.renderError(w, r, http.StatusBadRequest, text.ErrorBadRequestTitle, text.LoginElsewhere) case errors.Is(err, ErrLoginUnlinked): k.RenderLoginRejected(w, r, state, entries...) case errors.Is(err, ErrLoginBusy): form(http.StatusServiceUnavailable, text.LoginBusy) case errors.Is(err, ErrLoginStale): form(http.StatusOK, text.LoginStale) case errors.Is(err, ErrLoginRefused): slog.Warn("external sign-in refused", "err", err) form(http.StatusOK, text.LoginGoogleRefused) case errors.Is(err, ErrLoginCancelled): form(http.StatusOK, text.LoginGoogleCancelled) case errors.Is(err, ErrLoginUnreachable): slog.Error("external sign-in failed at the provider", "err", err) form(http.StatusBadGateway, text.LoginGoogleUnavailable) default: slog.Error("external sign-in failed", "err", err) k.renderError(w, r, http.StatusInternalServerError, text.ErrorInternalTitle, text.ErrorInternal) } }