mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: handle getUser error (#3285)
This commit is contained in:
+27
-13
@@ -51,6 +51,11 @@ type OAuth2Configs struct {
|
||||
Github OAuth2Config
|
||||
}
|
||||
|
||||
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,
|
||||
// updating the last used time in the database.
|
||||
@@ -83,7 +88,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: signedOutErrorMessage,
|
||||
Detail: fmt.Sprintf("Cookie %q or query parameter must be provided.", codersdk.SessionTokenKey),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -91,7 +97,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: signedOutErrorMessage,
|
||||
Detail: fmt.Sprintf("Invalid %q cookie API key format.", codersdk.SessionTokenKey),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -100,13 +107,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: signedOutErrorMessage,
|
||||
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: signedOutErrorMessage,
|
||||
Detail: fmt.Sprintf("Invalid %q cookie API key secret.", codersdk.SessionTokenKey),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -114,13 +123,14 @@ 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: signedOutErrorMessage,
|
||||
Detail: "API key is invalid.",
|
||||
})
|
||||
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
|
||||
}
|
||||
@@ -129,7 +139,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: signedOutErrorMessage,
|
||||
Detail: "API key secret is invalid.",
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -146,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
|
||||
}
|
||||
@@ -174,7 +186,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: signedOutErrorMessage,
|
||||
Detail: fmt.Sprintf("API key expired at %q.", key.ExpiresAt.String()),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -216,7 +229,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: internalErrorMessage,
|
||||
Detail: fmt.Sprintf("API key couldn't update: %s.", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -228,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
|
||||
}
|
||||
|
||||
@@ -13,9 +13,10 @@ export const RequireAuth: React.FC<RequireAuthProps> = ({ 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 <Navigate to={navigateTo} />
|
||||
return <Navigate to={navigateTo} state={{ isRedirect: !isHomePage }} />
|
||||
} else if (authState.hasTag("loading")) {
|
||||
return <FullScreenLoader />
|
||||
} else {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.",
|
||||
},
|
||||
|
||||
@@ -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 ? 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 <Navigate to={redirectTo} replace />
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user