Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fbd0c6d

Browse files
committed
fix: handle getUser error
1 parent 74c8766 commit fbd0c6d

File tree

5 files changed

+58
-15
lines changed

5 files changed

+58
-15
lines changed

coderd/httpmw/apikey.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ type OAuth2Configs struct {
5151
Github OAuth2Config
5252
}
5353

54+
const loggedOutErrorMessage string = "You are logged out. Please log in to continue."
55+
5456
// ExtractAPIKey requires authentication using a valid API key.
5557
// It handles extending an API key if it comes close to expiry,
5658
// updating the last used time in the database.
@@ -83,15 +85,17 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool
8385
}
8486
if cookieValue == "" {
8587
write(http.StatusUnauthorized, codersdk.Response{
86-
Message: fmt.Sprintf("Cookie %q or query parameter must be provided.", codersdk.SessionTokenKey),
88+
Message: loggedOutErrorMessage,
89+
Detail: fmt.Sprintf("Cookie %q or query parameter must be provided.", codersdk.SessionTokenKey),
8790
})
8891
return
8992
}
9093
parts := strings.Split(cookieValue, "-")
9194
// APIKeys are formatted: ID-SECRET
9295
if len(parts) != 2 {
9396
write(http.StatusUnauthorized, codersdk.Response{
94-
Message: fmt.Sprintf("Invalid %q cookie API key format.", codersdk.SessionTokenKey),
97+
Message: loggedOutErrorMessage,
98+
Detail: fmt.Sprintf("Invalid %q cookie API key format.", codersdk.SessionTokenKey),
9599
})
96100
return
97101
}
@@ -100,21 +104,24 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool
100104
// Ensuring key lengths are valid.
101105
if len(keyID) != 10 {
102106
write(http.StatusUnauthorized, codersdk.Response{
103-
Message: fmt.Sprintf("Invalid %q cookie API key id.", codersdk.SessionTokenKey),
107+
Message: loggedOutErrorMessage,
108+
Detail: fmt.Sprintf("Invalid %q cookie API key id.", codersdk.SessionTokenKey),
104109
})
105110
return
106111
}
107112
if len(keySecret) != 22 {
108113
write(http.StatusUnauthorized, codersdk.Response{
109-
Message: fmt.Sprintf("Invalid %q cookie API key secret.", codersdk.SessionTokenKey),
114+
Message: loggedOutErrorMessage,
115+
Detail: fmt.Sprintf("Invalid %q cookie API key secret.", codersdk.SessionTokenKey),
110116
})
111117
return
112118
}
113119
key, err := db.GetAPIKeyByID(r.Context(), keyID)
114120
if err != nil {
115121
if errors.Is(err, sql.ErrNoRows) {
116122
write(http.StatusUnauthorized, codersdk.Response{
117-
Message: "API key is invalid.",
123+
Message: loggedOutErrorMessage,
124+
Detail: "API key is invalid.",
118125
})
119126
return
120127
}
@@ -129,7 +136,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool
129136
// Checking to see if the secret is valid.
130137
if subtle.ConstantTimeCompare(key.HashedSecret, hashed[:]) != 1 {
131138
write(http.StatusUnauthorized, codersdk.Response{
132-
Message: "API key secret is invalid.",
139+
Message: loggedOutErrorMessage,
140+
Detail: "API key secret is invalid.",
133141
})
134142
return
135143
}
@@ -174,7 +182,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool
174182
// Checking if the key is expired.
175183
if key.ExpiresAt.Before(now) {
176184
write(http.StatusUnauthorized, codersdk.Response{
177-
Message: fmt.Sprintf("API key expired at %q.", key.ExpiresAt.String()),
185+
Message: loggedOutErrorMessage,
186+
Detail: fmt.Sprintf("API key expired at %q.", key.ExpiresAt.String()),
178187
})
179188
return
180189
}
@@ -216,7 +225,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool
216225
})
217226
if err != nil {
218227
write(http.StatusInternalServerError, codersdk.Response{
219-
Message: fmt.Sprintf("API key couldn't update: %s.", err.Error()),
228+
Message: loggedOutErrorMessage,
229+
Detail: fmt.Sprintf("API key couldn't update: %s.", err.Error()),
220230
})
221231
return
222232
}

site/src/components/RequireAuth/RequireAuth.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ export const RequireAuth: React.FC<RequireAuthProps> = ({ children }) => {
1313
const xServices = useContext(XServiceContext)
1414
const [authState] = useActor(xServices.authXService)
1515
const location = useLocation()
16-
const navigateTo = location.pathname === "/" ? "/login" : embedRedirect(location.pathname)
16+
const isHomePage = location.pathname === "/"
17+
const navigateTo = isHomePage ? "/login" : embedRedirect(location.pathname)
1718
if (authState.matches("signedOut")) {
18-
return <Navigate to={navigateTo} />
19+
return <Navigate to={navigateTo} state={{ isRedirect: !isHomePage }} />
1920
} else if (authState.hasTag("loading")) {
2021
return <FullScreenLoader />
2122
} else {

site/src/components/SignInForm/SignInForm.stories.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ WithLoginError.args = {
5151
},
5252
}
5353

54+
export const WithGetUserError = Template.bind({})
55+
WithGetUserError.args = {
56+
...SignedOut.args,
57+
loginErrors: {
58+
[LoginErrors.GET_USER_ERROR]: makeMockApiError({
59+
message: "You are logged out. Please log in to continue.",
60+
detail: "API Key is invalid.",
61+
}),
62+
},
63+
}
64+
5465
export const WithCheckPermissionsError = Template.bind({})
5566
WithCheckPermissionsError.args = {
5667
...SignedOut.args,
@@ -70,6 +81,18 @@ WithAuthMethodsError.args = {
7081
},
7182
}
7283

84+
export const WithGetUserAndAuthMethodsError = Template.bind({})
85+
WithGetUserAndAuthMethodsError.args = {
86+
...SignedOut.args,
87+
loginErrors: {
88+
[LoginErrors.GET_USER_ERROR]: makeMockApiError({
89+
message: "You are logged out. Please log in to continue.",
90+
detail: "API Key is invalid.",
91+
}),
92+
[LoginErrors.GET_METHODS_ERROR]: new Error("Failed to fetch auth methods"),
93+
},
94+
}
95+
7396
export const WithGithub = Template.bind({})
7497
WithGithub.args = {
7598
...SignedOut.args,

site/src/components/SignInForm/SignInForm.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface BuiltInAuthFormValues {
2525

2626
export enum LoginErrors {
2727
AUTH_ERROR = "authError",
28+
GET_USER_ERROR = "getUserError",
2829
CHECK_PERMISSIONS_ERROR = "checkPermissionsError",
2930
GET_METHODS_ERROR = "getMethodsError",
3031
}
@@ -36,6 +37,7 @@ export const Language = {
3637
emailRequired: "Please enter an email address.",
3738
errorMessages: {
3839
[LoginErrors.AUTH_ERROR]: "Incorrect email or password.",
40+
[LoginErrors.GET_USER_ERROR]: "Failed to fetch user details.",
3941
[LoginErrors.CHECK_PERMISSIONS_ERROR]: "Unable to fetch user permissions.",
4042
[LoginErrors.GET_METHODS_ERROR]: "Unable to fetch auth methods.",
4143
},

site/src/pages/LoginPage/LoginPage.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useContext } from "react"
44
import { Helmet } from "react-helmet"
55
import { Navigate, useLocation } from "react-router-dom"
66
import { Footer } from "../../components/Footer/Footer"
7-
import { SignInForm } from "../../components/SignInForm/SignInForm"
7+
import { LoginErrors, SignInForm } from "../../components/SignInForm/SignInForm"
88
import { pageTitle } from "../../util/page"
99
import { retrieveRedirect } from "../../util/redirect"
1010
import { XServiceContext } from "../../xServices/StateContext"
@@ -28,19 +28,25 @@ export const useStyles = makeStyles((theme) => ({
2828
},
2929
}))
3030

31+
interface LocationState {
32+
isRedirect: boolean
33+
}
34+
3135
export const LoginPage: React.FC = () => {
3236
const styles = useStyles()
3337
const location = useLocation()
3438
const xServices = useContext(XServiceContext)
3539
const [authState, authSend] = useActor(xServices.authXService)
3640
const isLoading = authState.hasTag("loading")
3741
const redirectTo = retrieveRedirect(location.search)
42+
const locationState = location.state ? (location.state as LocationState) : null
43+
const isRedirected = locationState !== null ? locationState.isRedirect : false
3844

3945
const onSubmit = async ({ email, password }: { email: string; password: string }) => {
4046
authSend({ type: "SIGN_IN", email, password })
4147
}
4248

43-
const { authError, checkPermissionsError, getMethodsError } = authState.context
49+
const { authError, getUserError, checkPermissionsError, getMethodsError } = authState.context
4450

4551
if (authState.matches("signedIn")) {
4652
return <Navigate to={redirectTo} replace />
@@ -57,9 +63,10 @@ export const LoginPage: React.FC = () => {
5763
redirectTo={redirectTo}
5864
isLoading={isLoading}
5965
loginErrors={{
60-
authError,
61-
checkPermissionsError,
62-
getMethodsError,
66+
[LoginErrors.AUTH_ERROR]: authError,
67+
[LoginErrors.GET_USER_ERROR]: isRedirected ? getUserError : null,
68+
[LoginErrors.CHECK_PERMISSIONS_ERROR]: checkPermissionsError,
69+
[LoginErrors.GET_METHODS_ERROR]: getMethodsError,
6370
}}
6471
onSubmit={onSubmit}
6572
/>

0 commit comments

Comments
 (0)