mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-07 13:22:43 +02:00
authenticate: show the device enrolled page as the user info page (#3151)
This commit is contained in:
parent
bb7de0d227
commit
da97546de1
7 changed files with 109 additions and 76 deletions
|
@ -100,7 +100,12 @@ func (a *Authenticate) mountDashboard(r *mux.Router) {
|
||||||
sr.Path("/sign_out").Handler(httputil.HandlerFunc(a.SignOut))
|
sr.Path("/sign_out").Handler(httputil.HandlerFunc(a.SignOut))
|
||||||
sr.Path("/webauthn").Handler(a.webauthn)
|
sr.Path("/webauthn").Handler(a.webauthn)
|
||||||
sr.Path("/device-enrolled").Handler(httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
sr.Path("/device-enrolled").Handler(httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
handlers.DeviceEnrolled().ServeHTTP(w, r)
|
userInfoData, err := a.getUserInfoData(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers.DeviceEnrolled(userInfoData).ServeHTTP(w, r)
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -505,6 +510,7 @@ func (a *Authenticate) getSessionFromCtx(ctx context.Context) (*sessions.State,
|
||||||
func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
|
func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
|
||||||
ctx, span := trace.StartSpan(r.Context(), "authenticate.userInfo")
|
ctx, span := trace.StartSpan(r.Context(), "authenticate.userInfo")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
r = r.WithContext(ctx)
|
||||||
|
|
||||||
// if we came in with a redirect URI, save it to a cookie so it doesn't expire with the HMAC
|
// if we came in with a redirect URI, save it to a cookie so it doesn't expire with the HMAC
|
||||||
if redirectURI := r.FormValue(urlutil.QueryRedirectURI); redirectURI != "" {
|
if redirectURI := r.FormValue(urlutil.QueryRedirectURI); redirectURI != "" {
|
||||||
|
@ -519,32 +525,42 @@ func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
state := a.state.Load()
|
userInfoData, err := a.getUserInfoData(r)
|
||||||
|
|
||||||
authenticateURL, err := a.options.Load().GetAuthenticateURL()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := a.getSessionFromCtx(ctx)
|
handlers.UserInfo(userInfoData).ServeHTTP(w, r)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authenticate) getUserInfoData(r *http.Request) (handlers.UserInfoData, error) {
|
||||||
|
state := a.state.Load()
|
||||||
|
|
||||||
|
authenticateURL, err := a.options.Load().GetAuthenticateURL()
|
||||||
|
if err != nil {
|
||||||
|
return handlers.UserInfoData{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := a.getSessionFromCtx(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.ID = uuid.New().String()
|
s.ID = uuid.New().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
pbSession, isImpersonated, err := a.getCurrentSession(ctx)
|
pbSession, isImpersonated, err := a.getCurrentSession(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pbSession = &session.Session{
|
pbSession = &session.Session{
|
||||||
Id: s.ID,
|
Id: s.ID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pbUser, err := a.getUser(ctx, pbSession.GetUserId())
|
pbUser, err := a.getUser(r.Context(), pbSession.GetUserId())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pbUser = &user.User{
|
pbUser = &user.User{
|
||||||
Id: pbSession.GetUserId(),
|
Id: pbSession.GetUserId(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pbDirectoryUser, err := a.getDirectoryUser(ctx, pbSession.GetUserId())
|
pbDirectoryUser, err := a.getDirectoryUser(r.Context(), pbSession.GetUserId())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pbDirectoryUser = &directory.User{
|
pbDirectoryUser = &directory.User{
|
||||||
Id: pbSession.GetUserId(),
|
Id: pbSession.GetUserId(),
|
||||||
|
@ -552,7 +568,7 @@ func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
var groups []*directory.Group
|
var groups []*directory.Group
|
||||||
for _, groupID := range pbDirectoryUser.GetGroupIds() {
|
for _, groupID := range pbDirectoryUser.GetGroupIds() {
|
||||||
pbDirectoryGroup, err := directory.GetGroup(ctx, state.dataBrokerClient, groupID)
|
pbDirectoryGroup, err := directory.GetGroup(r.Context(), state.dataBrokerClient, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pbDirectoryGroup = &directory.Group{
|
pbDirectoryGroup = &directory.Group{
|
||||||
Id: groupID,
|
Id: groupID,
|
||||||
|
@ -563,9 +579,9 @@ func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
|
||||||
groups = append(groups, pbDirectoryGroup)
|
groups = append(groups, pbDirectoryGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
creationOptions, requestOptions, _ := a.webauthn.GetOptions(ctx)
|
creationOptions, requestOptions, _ := a.webauthn.GetOptions(r.Context())
|
||||||
|
|
||||||
handlers.UserInfo(handlers.UserInfoData{
|
return handlers.UserInfoData{
|
||||||
CSRFToken: csrf.Token(r),
|
CSRFToken: csrf.Token(r),
|
||||||
DirectoryGroups: groups,
|
DirectoryGroups: groups,
|
||||||
DirectoryUser: pbDirectoryUser,
|
DirectoryUser: pbDirectoryUser,
|
||||||
|
@ -577,8 +593,7 @@ func (a *Authenticate) userInfo(w http.ResponseWriter, r *http.Request) error {
|
||||||
WebAuthnRequestOptions: requestOptions,
|
WebAuthnRequestOptions: requestOptions,
|
||||||
WebAuthnURL: urlutil.WebAuthnURL(r, authenticateURL, state.sharedKey, r.URL.Query()),
|
WebAuthnURL: urlutil.WebAuthnURL(r, authenticateURL, state.sharedKey, r.URL.Query()),
|
||||||
PomeriumVersion: version.FullVersion(),
|
PomeriumVersion: version.FullVersion(),
|
||||||
}).ServeHTTP(w, r)
|
}, nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Authenticate) saveSessionToDataBroker(
|
func (a *Authenticate) saveSessionToDataBroker(
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeviceEnrolled displays an HTML page informing the user that they've successfully enrolled a device.
|
// DeviceEnrolled displays an HTML page informing the user that they've successfully enrolled a device.
|
||||||
func DeviceEnrolled() http.Handler {
|
func DeviceEnrolled(data UserInfoData) http.Handler {
|
||||||
return httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
return httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||||
return ui.ServePage(w, r, "DeviceEnrolled", map[string]interface{}{})
|
return ui.ServePage(w, r, "DeviceEnrolled", data.ToJSON())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import DeviceEnrolledPage from "./components/DeviceEnrolledPage";
|
import Box from "@mui/material/Box";
|
||||||
|
import CssBaseline from "@mui/material/CssBaseline";
|
||||||
|
import { ThemeProvider } from "@mui/material/styles";
|
||||||
|
import { get } from "lodash";
|
||||||
|
import React, { FC } from "react";
|
||||||
|
|
||||||
import ErrorPage from "./components/ErrorPage";
|
import ErrorPage from "./components/ErrorPage";
|
||||||
import Footer from "./components/Footer";
|
import Footer from "./components/Footer";
|
||||||
import Header from "./components/Header";
|
import Header from "./components/Header";
|
||||||
|
@ -8,12 +13,7 @@ import UserInfoPage from "./components/UserInfoPage";
|
||||||
import WebAuthnRegistrationPage from "./components/WebAuthnRegistrationPage";
|
import WebAuthnRegistrationPage from "./components/WebAuthnRegistrationPage";
|
||||||
import { SubpageContextProvider } from "./context/Subpage";
|
import { SubpageContextProvider } from "./context/Subpage";
|
||||||
import { createTheme } from "./theme";
|
import { createTheme } from "./theme";
|
||||||
import {PageData, UserInfoPageData} from "./types";
|
import { PageData, UserInfoPageData } from "./types";
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import CssBaseline from "@mui/material/CssBaseline";
|
|
||||||
import { ThemeProvider } from "@mui/material/styles";
|
|
||||||
import React, { FC } from "react";
|
|
||||||
import {get} from "lodash";
|
|
||||||
|
|
||||||
const theme = createTheme();
|
const theme = createTheme();
|
||||||
|
|
||||||
|
@ -21,15 +21,13 @@ const App: FC = () => {
|
||||||
const data = (window["POMERIUM_DATA"] || {}) as PageData;
|
const data = (window["POMERIUM_DATA"] || {}) as PageData;
|
||||||
let body: React.ReactNode = <></>;
|
let body: React.ReactNode = <></>;
|
||||||
switch (data?.page) {
|
switch (data?.page) {
|
||||||
case "DeviceEnrolled":
|
|
||||||
body = <DeviceEnrolledPage data={data} />;
|
|
||||||
break;
|
|
||||||
case "Error":
|
case "Error":
|
||||||
body = <ErrorPage data={data} />;
|
body = <ErrorPage data={data} />;
|
||||||
break;
|
break;
|
||||||
case "SignOutConfirm":
|
case "SignOutConfirm":
|
||||||
body = <SignOutConfirmPage data={data} />;
|
body = <SignOutConfirmPage data={data} />;
|
||||||
break;
|
break;
|
||||||
|
case "DeviceEnrolled":
|
||||||
case "UserInfo":
|
case "UserInfo":
|
||||||
body = <UserInfoPage data={data} />;
|
body = <UserInfoPage data={data} />;
|
||||||
break;
|
break;
|
||||||
|
@ -40,7 +38,7 @@ const App: FC = () => {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<SubpageContextProvider>
|
<SubpageContextProvider page={data?.page}>
|
||||||
<Header includeSidebar={data?.page === "UserInfo"} data={data} />
|
<Header includeSidebar={data?.page === "UserInfo"} data={data} />
|
||||||
<ToolbarOffset />
|
<ToolbarOffset />
|
||||||
<Box sx={{ overflow: "hidden", height: "calc(100vh - 120px)" }}>
|
<Box sx={{ overflow: "hidden", height: "calc(100vh - 120px)" }}>
|
||||||
|
@ -55,7 +53,7 @@ const App: FC = () => {
|
||||||
<ToolbarOffset />
|
<ToolbarOffset />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Footer pomeriumVersion={get(data, 'pomeriumVersion')} />
|
<Footer pomeriumVersion={get(data, "pomeriumVersion")} />
|
||||||
</SubpageContextProvider>
|
</SubpageContextProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
import HeroSection from "./HeroSection";
|
|
||||||
import Container from "@mui/material/Container";
|
|
||||||
import React, { FC } from "react";
|
|
||||||
import { DeviceEnrolledPageData } from "src/types";
|
|
||||||
|
|
||||||
type DeviceEnrolledPageProps = {
|
|
||||||
data: DeviceEnrolledPageData;
|
|
||||||
};
|
|
||||||
const DeviceEnrolledPage: FC<DeviceEnrolledPageProps> = () => {
|
|
||||||
return (
|
|
||||||
<Container maxWidth={false}>
|
|
||||||
<HeroSection
|
|
||||||
title="Device Enrolled"
|
|
||||||
text="Device Successfully Enrolled"
|
|
||||||
/>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default DeviceEnrolledPage;
|
|
|
@ -1,9 +1,18 @@
|
||||||
import { Drawer, useMediaQuery } from "@mui/material";
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
|
Drawer,
|
||||||
|
useMediaQuery,
|
||||||
|
} from "@mui/material";
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { useTheme } from "@mui/material/styles";
|
||||||
import React, { FC, useContext } from "react";
|
import React, { FC, useContext, useEffect, useState } from "react";
|
||||||
import { UserInfoPageData } from "src/types";
|
import { UserInfoData } from "src/types";
|
||||||
|
|
||||||
import { SubpageContext } from "../context/Subpage";
|
import { SubpageContext } from "../context/Subpage";
|
||||||
import GroupDetails from "./GroupDetails";
|
import GroupDetails from "./GroupDetails";
|
||||||
|
@ -13,7 +22,7 @@ import { ToolbarOffset } from "./ToolbarOffset";
|
||||||
import { UserSidebarContent } from "./UserSidebarContent";
|
import { UserSidebarContent } from "./UserSidebarContent";
|
||||||
|
|
||||||
type UserInfoPageProps = {
|
type UserInfoPageProps = {
|
||||||
data: UserInfoPageData;
|
data: UserInfoData & { page: "DeviceEnrolled" | "UserInfo" };
|
||||||
};
|
};
|
||||||
const UserInfoPage: FC<UserInfoPageProps> = ({ data }) => {
|
const UserInfoPage: FC<UserInfoPageProps> = ({ data }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -23,8 +32,31 @@ const UserInfoPage: FC<UserInfoPageProps> = ({ data }) => {
|
||||||
});
|
});
|
||||||
const { subpage } = useContext(SubpageContext);
|
const { subpage } = useContext(SubpageContext);
|
||||||
|
|
||||||
|
const [showDeviceEnrolled, setShowDeviceEnrolled] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data.page === "DeviceEnrolled") {
|
||||||
|
setShowDeviceEnrolled(true);
|
||||||
|
} else {
|
||||||
|
setShowDeviceEnrolled(false);
|
||||||
|
}
|
||||||
|
}, [data.page]);
|
||||||
|
|
||||||
|
function handleCloseDeviceEnrolled() {
|
||||||
|
setShowDeviceEnrolled(false);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth={false}>
|
<Container maxWidth={false}>
|
||||||
|
<Dialog open={showDeviceEnrolled} onClose={handleCloseDeviceEnrolled}>
|
||||||
|
<DialogTitle>Device Enrolled</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>Device Successfully Enrolled</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleCloseDeviceEnrolled}>OK</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
{mdUp && (
|
{mdUp && (
|
||||||
<Drawer
|
<Drawer
|
||||||
anchor="left"
|
anchor="left"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, {createContext, FC, useState} from 'react'
|
import React, { createContext, FC, useState } from "react";
|
||||||
|
|
||||||
export interface SubpageContextValue {
|
export interface SubpageContextValue {
|
||||||
subpage: string;
|
subpage: string;
|
||||||
|
@ -10,22 +10,25 @@ export const SubpageContext = createContext<SubpageContextValue>({
|
||||||
setSubpage: (_: string) => {},
|
setSubpage: (_: string) => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SubpageContextProvider:FC = ({children}) => {
|
export type SubpageContextProviderProps = {
|
||||||
|
page: string;
|
||||||
|
};
|
||||||
|
export const SubpageContextProvider: FC<SubpageContextProviderProps> = ({
|
||||||
|
page,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
const setSubpage = (subpage: string) => {
|
const setSubpage = (subpage: string) => {
|
||||||
setState({...state, subpage})
|
setState({ ...state, subpage });
|
||||||
}
|
};
|
||||||
|
|
||||||
const initState = {
|
const initState = {
|
||||||
subpage: "User",
|
subpage: page === "DeviceEnrolled" ? "Devices Info" : "User",
|
||||||
setSubpage
|
setSubpage,
|
||||||
}
|
};
|
||||||
|
|
||||||
const [state, setState] = useState(initState)
|
const [state, setState] = useState(initState);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubpageContext.Provider value={state}>
|
<SubpageContext.Provider value={state}>{children}</SubpageContext.Provider>
|
||||||
{children}
|
);
|
||||||
</SubpageContext.Provider>
|
};
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -95,18 +95,7 @@ export type ErrorPageData = BasePageData & {
|
||||||
version?: string;
|
version?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeviceEnrolledPageData = BasePageData & {
|
export type UserInfoData = {
|
||||||
page: "DeviceEnrolled";
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SignOutConfirmPageData = BasePageData & {
|
|
||||||
page: "SignOutConfirm";
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UserInfoPageData = BasePageData & {
|
|
||||||
page: "UserInfo";
|
|
||||||
|
|
||||||
csrfToken: string;
|
csrfToken: string;
|
||||||
directoryGroups?: Group[];
|
directoryGroups?: Group[];
|
||||||
directoryUser?: DirectoryUser;
|
directoryUser?: DirectoryUser;
|
||||||
|
@ -118,6 +107,21 @@ export type UserInfoPageData = BasePageData & {
|
||||||
pomeriumVersion: string;
|
pomeriumVersion: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DeviceEnrolledPageData = BasePageData &
|
||||||
|
UserInfoData & {
|
||||||
|
page: "DeviceEnrolled";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SignOutConfirmPageData = BasePageData & {
|
||||||
|
page: "SignOutConfirm";
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserInfoPageData = BasePageData &
|
||||||
|
UserInfoData & {
|
||||||
|
page: "UserInfo";
|
||||||
|
};
|
||||||
|
|
||||||
export type WebAuthnRegistrationPageData = BasePageData & {
|
export type WebAuthnRegistrationPageData = BasePageData & {
|
||||||
page: "WebAuthnRegistration";
|
page: "WebAuthnRegistration";
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue