-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
323 additions
and
88 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { formatDistance } from "date-fns"; | ||
import { jwtDecode } from "jwt-decode"; | ||
|
||
export type AKProxy = { | ||
user_attributes: { | ||
ldap_uniq: string; | ||
distinguishedName: string; | ||
cardId: string; | ||
membershipExpiration: string; | ||
membershipExpirationDate: string; | ||
membershipExpirationTimestamp: number; | ||
}; | ||
is_superuser: boolean; | ||
}; | ||
|
||
export type JWTContents = { | ||
email: string; | ||
name: string; | ||
given_name: string; | ||
preferred_username: string; | ||
nickname: string; | ||
groups: string[]; | ||
ak_proxy: AKProxy; | ||
}; | ||
|
||
export function getBarData(jwt: string) { | ||
const parsedJwt = jwtDecode<JWTContents>(jwt); | ||
|
||
const { groups, preferred_username, ak_proxy } = parsedJwt; | ||
const expirationTimestamp = | ||
ak_proxy.user_attributes.membershipExpirationTimestamp * 1000; | ||
|
||
const relative = formatDistance(new Date(expirationTimestamp), Date.now(), { | ||
addSuffix: true, | ||
}); | ||
return { | ||
groups, | ||
nickname: preferred_username, | ||
expiration: relative, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
import { jwtDecode } from "jwt-decode"; | ||
import { ImageResponse } from "next/og"; | ||
import { formatDistance } from "date-fns"; | ||
|
||
import { readFileSync } from "fs"; | ||
import { join } from "path"; | ||
import { WhoamiProps } from "@/components/whoami"; | ||
import getConfig from "next/config"; | ||
import { IconUsercircle } from "@/components/icons/IconUserCircle"; | ||
import { IconClock } from "@/components/icons/IconClock"; | ||
import { JWTContents, getBarData } from "@/app/LoginHeaders"; | ||
import { headers } from "next/headers"; | ||
|
||
function WhoamiImage({ | ||
nickname, | ||
expiration, | ||
groups, | ||
theme, | ||
}: WhoamiProps & { | ||
theme: "dark" | "light"; | ||
}) { | ||
const styles = { | ||
dark: { | ||
bg: "rgb(31, 41, 55)", | ||
text: "#fff", | ||
textLabel: "rgb(209, 213, 219)", | ||
}, | ||
light: { | ||
bg: "rgb(255, 255, 255)", | ||
badgeBorder: "rgb(229, 231, 235)", | ||
text: "#121212", | ||
textLabel: "rgb(31, 41, 55)", | ||
}, | ||
}; | ||
|
||
const currentTheme = styles[theme]; | ||
|
||
return ( | ||
<div | ||
style={{ | ||
fontFamily: "Inter", | ||
display: "flex", | ||
flexDirection: "column", | ||
backgroundColor: currentTheme.bg, | ||
padding: "16px", | ||
color: currentTheme.text, | ||
boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.2)", | ||
maxWidth: "800px", | ||
margin: "auto", | ||
height: "100%", | ||
width: "100%", | ||
justifyContent: "center", | ||
alignItems: "center", | ||
gap: "16px", | ||
}} | ||
> | ||
<h1 | ||
style={{ | ||
marginTop: "-4px", | ||
}} | ||
> | ||
Current user data: | ||
</h1> | ||
<div | ||
style={{ | ||
display: "flex", | ||
flexDirection: "column", | ||
justifyContent: "space-between", | ||
fontSize: "16px", | ||
gap: "8px", | ||
}} | ||
> | ||
<div | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
gap: "8px", | ||
paddingBottom: "8px", | ||
fontSize: "20px", | ||
}} | ||
> | ||
<IconUsercircle className="h-5 w-5 text-gray-500 dark:text-gray-400" /> | ||
|
||
<span | ||
style={{ | ||
fontFamily: "Inter", | ||
fontWeight: "400", | ||
display: "block", | ||
color: currentTheme.textLabel, | ||
}} | ||
> | ||
Preferred Name:{" "} | ||
</span> | ||
<span | ||
style={{ | ||
display: "block", | ||
fontWeight: "bold", | ||
}} | ||
> | ||
{nickname} | ||
</span> | ||
</div> | ||
<div | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
gap: "8px", | ||
fontSize: "20px", | ||
}} | ||
> | ||
<IconClock className="h-5 w-5 text-gray-500 dark:text-gray-400" /> | ||
<span | ||
style={{ | ||
display: "block", | ||
color: currentTheme.textLabel, | ||
}} | ||
> | ||
Access Token Expiry:{" "} | ||
</span> | ||
<span | ||
style={{ | ||
display: "block", | ||
fontWeight: "bold", | ||
}} | ||
> | ||
{expiration} | ||
</span> | ||
</div> | ||
{groups.length > 0 && ( | ||
<div | ||
style={{ | ||
display: "flex", | ||
flexDirection: "column", | ||
justifyContent: "space-between", | ||
fontSize: "16px", | ||
}} | ||
> | ||
<span | ||
style={{ | ||
display: "block", | ||
fontWeight: "bold", | ||
fontSize: "16px", | ||
paddingBottom: "8px", | ||
}} | ||
> | ||
Groups: | ||
</span> | ||
<div | ||
style={{ | ||
display: "flex", | ||
flexWrap: "wrap", | ||
gap: "8px", | ||
}} | ||
> | ||
{[...groups].map((group, index) => ( | ||
<span | ||
key={group} | ||
style={{ | ||
display: "block", | ||
padding: "4px 8px", | ||
borderRadius: "8px", | ||
fontWeight: "bold", | ||
fontSize: "14px", | ||
color: currentTheme.text, | ||
border: "1px solid rgb(229, 231, 235)", | ||
}} | ||
> | ||
{group} | ||
</span> | ||
))} | ||
</div> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
const publicPath = join(process.cwd(), "./public", "fonts", "inter"); | ||
const interRegular = readFileSync(join(publicPath, "Inter-Regular.ttf")); | ||
const interBold = readFileSync(join(publicPath, "Inter-Bold.ttf")); | ||
|
||
export const dynamic = "force-dynamic"; // defaults to force-static | ||
export async function GET(request: Request) { | ||
const { searchParams } = new URL(request.url); | ||
|
||
const theme = | ||
searchParams.get("theme")?.toLowerCase() === "dark" ? "dark" : "light"; | ||
const headersList = headers(); | ||
const jwt = headersList.get("X-Authentik-Jwt"); | ||
|
||
if (!jwt) { | ||
return new Response( | ||
JSON.stringify({ | ||
status: 401, | ||
message: "Unauthorized", | ||
}), | ||
{ | ||
status: 401, | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
} | ||
); | ||
} | ||
|
||
const { groups, nickname, expiration } = getBarData(jwt); | ||
|
||
return new ImageResponse( | ||
( | ||
<WhoamiImage | ||
groups={groups} | ||
nickname={nickname} | ||
expiration={expiration} | ||
theme={theme} | ||
/> | ||
), | ||
{ | ||
fonts: [ | ||
{ | ||
name: "Inter", | ||
weight: 400, | ||
data: interRegular, | ||
}, | ||
{ | ||
name: "Inter", | ||
weight: 800, | ||
data: interBold, | ||
}, | ||
], | ||
width: 400, | ||
height: 250, | ||
} | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export function IconClock(props: { className?: string }) { | ||
return ( | ||
<svg | ||
{...props} | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="24" | ||
height="24" | ||
viewBox="0 0 24 24" | ||
fill="none" | ||
stroke="currentColor" | ||
stroke-width="2" | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
> | ||
<circle cx="12" cy="12" r="10" /> | ||
<polyline points="12 6 12 12 16 14" /> | ||
</svg> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
export function IconUsercircle(props: { className?: string }) { | ||
return ( | ||
<svg | ||
{...props} | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="24" | ||
height="24" | ||
viewBox="0 0 24 24" | ||
fill="none" | ||
stroke="currentColor" | ||
stroke-width="2" | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
> | ||
<circle cx="12" cy="12" r="10" /> | ||
<circle cx="12" cy="10" r="3" /> | ||
<path d="M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662" /> | ||
</svg> | ||
); | ||
} |
Oops, something went wrong.