From fbd0c6d10652e6533084fbda48b30a0f4dd4f1a5 Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Fri, 29 Jul 2022 02:38:19 +0000 Subject: [PATCH 1/5] fix: handle getUser error --- coderd/httpmw/apikey.go | 26 +++++++++++++------ .../components/RequireAuth/RequireAuth.tsx | 5 ++-- .../SignInForm/SignInForm.stories.tsx | 23 ++++++++++++++++ site/src/components/SignInForm/SignInForm.tsx | 2 ++ site/src/pages/LoginPage/LoginPage.tsx | 17 ++++++++---- 5 files changed, 58 insertions(+), 15 deletions(-) diff --git a/coderd/httpmw/apikey.go b/coderd/httpmw/apikey.go index 6634363e4ce9e..1917a114d303f 100644 --- a/coderd/httpmw/apikey.go +++ b/coderd/httpmw/apikey.go @@ -51,6 +51,8 @@ type OAuth2Configs struct { Github OAuth2Config } +const loggedOutErrorMessage string = "You are logged out. Please log in to continue." + // ExtractAPIKey requires authentication using a valid API key. // It handles extending an API key if it comes close to expiry, // updating the last used time in the database. @@ -83,7 +85,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool } if cookieValue == "" { write(http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("Cookie %q or query parameter must be provided.", codersdk.SessionTokenKey), + Message: loggedOutErrorMessage, + Detail: fmt.Sprintf("Cookie %q or query parameter must be provided.", codersdk.SessionTokenKey), }) return } @@ -91,7 +94,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // APIKeys are formatted: ID-SECRET if len(parts) != 2 { write(http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("Invalid %q cookie API key format.", codersdk.SessionTokenKey), + Message: loggedOutErrorMessage, + Detail: fmt.Sprintf("Invalid %q cookie API key format.", codersdk.SessionTokenKey), }) return } @@ -100,13 +104,15 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Ensuring key lengths are valid. if len(keyID) != 10 { write(http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("Invalid %q cookie API key id.", codersdk.SessionTokenKey), + Message: loggedOutErrorMessage, + Detail: fmt.Sprintf("Invalid %q cookie API key id.", codersdk.SessionTokenKey), }) return } if len(keySecret) != 22 { write(http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("Invalid %q cookie API key secret.", codersdk.SessionTokenKey), + Message: loggedOutErrorMessage, + Detail: fmt.Sprintf("Invalid %q cookie API key secret.", codersdk.SessionTokenKey), }) return } @@ -114,7 +120,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool if err != nil { if errors.Is(err, sql.ErrNoRows) { write(http.StatusUnauthorized, codersdk.Response{ - Message: "API key is invalid.", + Message: loggedOutErrorMessage, + Detail: "API key is invalid.", }) return } @@ -129,7 +136,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Checking to see if the secret is valid. if subtle.ConstantTimeCompare(key.HashedSecret, hashed[:]) != 1 { write(http.StatusUnauthorized, codersdk.Response{ - Message: "API key secret is invalid.", + Message: loggedOutErrorMessage, + Detail: "API key secret is invalid.", }) return } @@ -174,7 +182,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Checking if the key is expired. if key.ExpiresAt.Before(now) { write(http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("API key expired at %q.", key.ExpiresAt.String()), + Message: loggedOutErrorMessage, + Detail: fmt.Sprintf("API key expired at %q.", key.ExpiresAt.String()), }) return } @@ -216,7 +225,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool }) if err != nil { write(http.StatusInternalServerError, codersdk.Response{ - Message: fmt.Sprintf("API key couldn't update: %s.", err.Error()), + Message: loggedOutErrorMessage, + Detail: fmt.Sprintf("API key couldn't update: %s.", err.Error()), }) return } diff --git a/site/src/components/RequireAuth/RequireAuth.tsx b/site/src/components/RequireAuth/RequireAuth.tsx index 1776720d90a7a..9a51ab658d2db 100644 --- a/site/src/components/RequireAuth/RequireAuth.tsx +++ b/site/src/components/RequireAuth/RequireAuth.tsx @@ -13,9 +13,10 @@ export const RequireAuth: React.FC = ({ children }) => { const xServices = useContext(XServiceContext) const [authState] = useActor(xServices.authXService) const location = useLocation() - const navigateTo = location.pathname === "/" ? "/login" : embedRedirect(location.pathname) + const isHomePage = location.pathname === "/" + const navigateTo = isHomePage ? "/login" : embedRedirect(location.pathname) if (authState.matches("signedOut")) { - return + return } else if (authState.hasTag("loading")) { return } else { diff --git a/site/src/components/SignInForm/SignInForm.stories.tsx b/site/src/components/SignInForm/SignInForm.stories.tsx index 1937dfd066a5b..88378c4acd23a 100644 --- a/site/src/components/SignInForm/SignInForm.stories.tsx +++ b/site/src/components/SignInForm/SignInForm.stories.tsx @@ -51,6 +51,17 @@ WithLoginError.args = { }, } +export const WithGetUserError = Template.bind({}) +WithGetUserError.args = { + ...SignedOut.args, + loginErrors: { + [LoginErrors.GET_USER_ERROR]: makeMockApiError({ + message: "You are logged out. Please log in to continue.", + detail: "API Key is invalid.", + }), + }, +} + export const WithCheckPermissionsError = Template.bind({}) WithCheckPermissionsError.args = { ...SignedOut.args, @@ -70,6 +81,18 @@ WithAuthMethodsError.args = { }, } +export const WithGetUserAndAuthMethodsError = Template.bind({}) +WithGetUserAndAuthMethodsError.args = { + ...SignedOut.args, + loginErrors: { + [LoginErrors.GET_USER_ERROR]: makeMockApiError({ + message: "You are logged out. Please log in to continue.", + detail: "API Key is invalid.", + }), + [LoginErrors.GET_METHODS_ERROR]: new Error("Failed to fetch auth methods"), + }, +} + export const WithGithub = Template.bind({}) WithGithub.args = { ...SignedOut.args, diff --git a/site/src/components/SignInForm/SignInForm.tsx b/site/src/components/SignInForm/SignInForm.tsx index a4d75dad63dd7..2860f6f3cedd2 100644 --- a/site/src/components/SignInForm/SignInForm.tsx +++ b/site/src/components/SignInForm/SignInForm.tsx @@ -25,6 +25,7 @@ interface BuiltInAuthFormValues { export enum LoginErrors { AUTH_ERROR = "authError", + GET_USER_ERROR = "getUserError", CHECK_PERMISSIONS_ERROR = "checkPermissionsError", GET_METHODS_ERROR = "getMethodsError", } @@ -36,6 +37,7 @@ export const Language = { emailRequired: "Please enter an email address.", errorMessages: { [LoginErrors.AUTH_ERROR]: "Incorrect email or password.", + [LoginErrors.GET_USER_ERROR]: "Failed to fetch user details.", [LoginErrors.CHECK_PERMISSIONS_ERROR]: "Unable to fetch user permissions.", [LoginErrors.GET_METHODS_ERROR]: "Unable to fetch auth methods.", }, diff --git a/site/src/pages/LoginPage/LoginPage.tsx b/site/src/pages/LoginPage/LoginPage.tsx index 305012425130d..ecbf3952bbec8 100644 --- a/site/src/pages/LoginPage/LoginPage.tsx +++ b/site/src/pages/LoginPage/LoginPage.tsx @@ -4,7 +4,7 @@ import React, { useContext } from "react" import { Helmet } from "react-helmet" import { Navigate, useLocation } from "react-router-dom" import { Footer } from "../../components/Footer/Footer" -import { SignInForm } from "../../components/SignInForm/SignInForm" +import { LoginErrors, SignInForm } from "../../components/SignInForm/SignInForm" import { pageTitle } from "../../util/page" import { retrieveRedirect } from "../../util/redirect" import { XServiceContext } from "../../xServices/StateContext" @@ -28,6 +28,10 @@ export const useStyles = makeStyles((theme) => ({ }, })) +interface LocationState { + isRedirect: boolean +} + export const LoginPage: React.FC = () => { const styles = useStyles() const location = useLocation() @@ -35,12 +39,14 @@ export const LoginPage: React.FC = () => { const [authState, authSend] = useActor(xServices.authXService) const isLoading = authState.hasTag("loading") const redirectTo = retrieveRedirect(location.search) + const locationState = location.state ? (location.state as LocationState) : null + const isRedirected = locationState !== null ? locationState.isRedirect : false const onSubmit = async ({ email, password }: { email: string; password: string }) => { authSend({ type: "SIGN_IN", email, password }) } - const { authError, checkPermissionsError, getMethodsError } = authState.context + const { authError, getUserError, checkPermissionsError, getMethodsError } = authState.context if (authState.matches("signedIn")) { return @@ -57,9 +63,10 @@ export const LoginPage: React.FC = () => { redirectTo={redirectTo} isLoading={isLoading} loginErrors={{ - authError, - checkPermissionsError, - getMethodsError, + [LoginErrors.AUTH_ERROR]: authError, + [LoginErrors.GET_USER_ERROR]: isRedirected ? getUserError : null, + [LoginErrors.CHECK_PERMISSIONS_ERROR]: checkPermissionsError, + [LoginErrors.GET_METHODS_ERROR]: getMethodsError, }} onSubmit={onSubmit} /> From 8dcd9586208bc84356bf6564b20dc2c9e4954713 Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Fri, 29 Jul 2022 02:57:00 +0000 Subject: [PATCH 2/5] change language to use sign-out --- coderd/httpmw/apikey.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/coderd/httpmw/apikey.go b/coderd/httpmw/apikey.go index 1917a114d303f..e1d56539c00a0 100644 --- a/coderd/httpmw/apikey.go +++ b/coderd/httpmw/apikey.go @@ -51,7 +51,7 @@ type OAuth2Configs struct { Github OAuth2Config } -const loggedOutErrorMessage string = "You are logged out. Please log in to continue." +const signedOutErrorMessage string = "You are signed out. Please sign in to continue." // ExtractAPIKey requires authentication using a valid API key. // It handles extending an API key if it comes close to expiry, @@ -85,7 +85,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool } if cookieValue == "" { write(http.StatusUnauthorized, codersdk.Response{ - Message: loggedOutErrorMessage, + Message: signedOutErrorMessage, Detail: fmt.Sprintf("Cookie %q or query parameter must be provided.", codersdk.SessionTokenKey), }) return @@ -94,7 +94,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // APIKeys are formatted: ID-SECRET if len(parts) != 2 { write(http.StatusUnauthorized, codersdk.Response{ - Message: loggedOutErrorMessage, + Message: signedOutErrorMessage, Detail: fmt.Sprintf("Invalid %q cookie API key format.", codersdk.SessionTokenKey), }) return @@ -104,14 +104,14 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Ensuring key lengths are valid. if len(keyID) != 10 { write(http.StatusUnauthorized, codersdk.Response{ - Message: loggedOutErrorMessage, + Message: signedOutErrorMessage, Detail: fmt.Sprintf("Invalid %q cookie API key id.", codersdk.SessionTokenKey), }) return } if len(keySecret) != 22 { write(http.StatusUnauthorized, codersdk.Response{ - Message: loggedOutErrorMessage, + Message: signedOutErrorMessage, Detail: fmt.Sprintf("Invalid %q cookie API key secret.", codersdk.SessionTokenKey), }) return @@ -120,7 +120,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool if err != nil { if errors.Is(err, sql.ErrNoRows) { write(http.StatusUnauthorized, codersdk.Response{ - Message: loggedOutErrorMessage, + Message: signedOutErrorMessage, Detail: "API key is invalid.", }) return @@ -136,7 +136,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Checking to see if the secret is valid. if subtle.ConstantTimeCompare(key.HashedSecret, hashed[:]) != 1 { write(http.StatusUnauthorized, codersdk.Response{ - Message: loggedOutErrorMessage, + Message: signedOutErrorMessage, Detail: "API key secret is invalid.", }) return @@ -182,7 +182,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Checking if the key is expired. if key.ExpiresAt.Before(now) { write(http.StatusUnauthorized, codersdk.Response{ - Message: loggedOutErrorMessage, + Message: signedOutErrorMessage, Detail: fmt.Sprintf("API key expired at %q.", key.ExpiresAt.String()), }) return @@ -225,7 +225,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool }) if err != nil { write(http.StatusInternalServerError, codersdk.Response{ - Message: loggedOutErrorMessage, + Message: signedOutErrorMessage, Detail: fmt.Sprintf("API key couldn't update: %s.", err.Error()), }) return From 52c23fb55a327bc424ec580c04dce383a3506e0b Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Fri, 29 Jul 2022 03:04:42 +0000 Subject: [PATCH 3/5] update sign out message --- coderd/httpmw/apikey.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/httpmw/apikey.go b/coderd/httpmw/apikey.go index e1d56539c00a0..ead5759eb1a51 100644 --- a/coderd/httpmw/apikey.go +++ b/coderd/httpmw/apikey.go @@ -51,7 +51,7 @@ type OAuth2Configs struct { Github OAuth2Config } -const signedOutErrorMessage string = "You are signed out. Please sign in to continue." +const signedOutErrorMessage string = "You are signed out or your session has expired. Please sign in again to continue." // ExtractAPIKey requires authentication using a valid API key. // It handles extending an API key if it comes close to expiry, From ade96de46c204deec024473dac1f44708702a1b6 Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Fri, 29 Jul 2022 15:57:33 +0000 Subject: [PATCH 4/5] update internal error message --- coderd/httpmw/apikey.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/coderd/httpmw/apikey.go b/coderd/httpmw/apikey.go index ead5759eb1a51..9ababe6cd45ef 100644 --- a/coderd/httpmw/apikey.go +++ b/coderd/httpmw/apikey.go @@ -51,7 +51,10 @@ type OAuth2Configs struct { Github OAuth2Config } -const signedOutErrorMessage string = "You are signed out or your session has expired. Please sign in again to continue." +const ( + signedOutErrorMessage string = "You are signed out or your session has expired. Please sign in again to continue." + internalErrorMessage string = "An internal error occurred. Please try again or contact the system administrator." +) // ExtractAPIKey requires authentication using a valid API key. // It handles extending an API key if it comes close to expiry, @@ -126,8 +129,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool return } write(http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching API key by id.", - Detail: err.Error(), + Message: internalErrorMessage, + Detail: fmt.Sprintf("Internal error fetching API key by id. %s", err.Error()), }) return } @@ -154,7 +157,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool oauthConfig = oauth.Github default: write(http.StatusInternalServerError, codersdk.Response{ - Message: fmt.Sprintf("Unexpected authentication type %q.", key.LoginType), + Message: internalErrorMessage, + Detail: fmt.Sprintf("Unexpected authentication type %q.", key.LoginType), }) return } @@ -225,7 +229,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool }) if err != nil { write(http.StatusInternalServerError, codersdk.Response{ - Message: signedOutErrorMessage, + Message: internalErrorMessage, Detail: fmt.Sprintf("API key couldn't update: %s.", err.Error()), }) return @@ -238,8 +242,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool roles, err := db.GetAuthorizationUserRoles(r.Context(), key.UserID) if err != nil { write(http.StatusUnauthorized, codersdk.Response{ - Message: "Internal error fetching user's roles.", - Detail: err.Error(), + Message: internalErrorMessage, + Detail: fmt.Sprintf("Internal error fetching user's roles. %s", err.Error()), }) return } From 4bcf5e722249d1072379bf99ba3dfecb667e6950 Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Fri, 29 Jul 2022 16:19:48 +0000 Subject: [PATCH 5/5] simplify conditional --- site/src/pages/LoginPage/LoginPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/LoginPage/LoginPage.tsx b/site/src/pages/LoginPage/LoginPage.tsx index ecbf3952bbec8..b84dac9c87106 100644 --- a/site/src/pages/LoginPage/LoginPage.tsx +++ b/site/src/pages/LoginPage/LoginPage.tsx @@ -40,7 +40,7 @@ export const LoginPage: React.FC = () => { const isLoading = authState.hasTag("loading") const redirectTo = retrieveRedirect(location.search) const locationState = location.state ? (location.state as LocationState) : null - const isRedirected = locationState !== null ? locationState.isRedirect : false + const isRedirected = locationState ? locationState.isRedirect : false const onSubmit = async ({ email, password }: { email: string; password: string }) => { authSend({ type: "SIGN_IN", email, password }) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy