Skip to content

Commit

Permalink
fix: pwa
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentchalamon committed Dec 21, 2023
1 parent a30b4cf commit 381a4e3
Show file tree
Hide file tree
Showing 31 changed files with 431 additions and 319 deletions.
3 changes: 2 additions & 1 deletion compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ services:
NEXTAUTH_URL: ${NEXTAUTH_URL:-https://localhost/api/auth}
NEXTAUTH_URL_INTERNAL: http://127.0.0.1:3000/api/auth
OIDC_CLIENT_ID: ${OIDC_CLIENT_ID:-api-platform-pwa}
OIDC_SERVER_URL: ${OIDC_SERVER_URL_INTERNAL:-http://php/oidc/realms/demo}
OIDC_SERVER_URL: ${OIDC_SERVER_URL:-https://localhost/oidc/realms/demo}
OIDC_SERVER_URL_INTERNAL: ${OIDC_SERVER_URL_INTERNAL:-http://php/oidc/realms/demo}

###> doctrine/doctrine-bundle ###
database:
Expand Down
5 changes: 5 additions & 0 deletions helm/api-platform/templates/pwa-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ spec:
configMapKeyRef:
name: {{ include "api-platform.fullname" . }}
key: oidc-server-url
- name: OIDC_SERVER_URL_INTERNAL
valueFrom:
configMapKeyRef:
name: {{ include "api-platform.fullname" . }}
key: oidc-server-url-internal
- name: OIDC_CLIENT_ID
valueFrom:
configMapKeyRef:
Expand Down
1 change: 1 addition & 0 deletions pwa/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { GET, POST } from "../../../auth";
29 changes: 17 additions & 12 deletions pwa/app/auth.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { type TokenSet } from "@auth/core/types";
import { signOut as logout, type SignOutParams } from "next-auth/react";
import NextAuth, { type Session as DefaultSession, type User as DefaultUser, type NextAuthResult as DefaultNextAuthResult } from "next-auth";
import NextAuth, { type Session as DefaultSession, type User as DefaultUser } from "next-auth";
import KeycloakProvider from "next-auth/providers/keycloak";

import { OIDC_CLIENT_ID, OIDC_SERVER_URL } from "../config/keycloak";
import { OIDC_CLIENT_ID, OIDC_SERVER_URL, OIDC_SERVER_URL_INTERNAL } from "../config/keycloak";

export interface User extends DefaultUser {
sub?: string | null
Expand Down Expand Up @@ -41,7 +41,7 @@ export async function signOut<R extends boolean = true>(
options?: SignOutParams<R>
): Promise<R extends true ? undefined : SignOutResponse> {
// @ts-ignore
const url = `${OIDC_SERVER_URL}/protocol/openid-connect/logout?id_token_hint=${session.idToken}&post_logout_redirect_uri=${options?.callbackUrl ?? `${window.location.origin}/books`}`;
const url = `${OIDC_SERVER_URL_INTERNAL}/protocol/openid-connect/logout?id_token_hint=${session.idToken}&post_logout_redirect_uri=${options?.callbackUrl ?? `${window.location.origin}/books`}`;

return await logout({ callbackUrl: url });
}
Expand All @@ -66,7 +66,7 @@ export const { handlers: { GET, POST }, auth } = NextAuth({
// If the access token has expired, try to refresh it
try {
// todo use .well-known
const response = await fetch(`${OIDC_SERVER_URL}/protocol/openid-connect/token`, {
const response = await fetch(`${OIDC_SERVER_URL_INTERNAL}/protocol/openid-connect/token`, {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: OIDC_CLIENT_ID,
Expand Down Expand Up @@ -121,21 +121,26 @@ export const { handlers: { GET, POST }, auth } = NextAuth({
KeycloakProvider({
id: 'keycloak',
clientId: OIDC_CLIENT_ID,
issuer: OIDC_SERVER_URL,
issuer: OIDC_SERVER_URL_INTERNAL,
// https://github.com/nextauthjs/next-auth/issues/685#issuecomment-785212676
// protection: "pkce",
client: {
token_endpoint_auth_method: "none"
},
// discovery not working: https://github.com/nextauthjs/next-auth/issues/8374
authorization: {
url: `${OIDC_SERVER_URL}/protocol/openid-connect/auth`,
// https://authjs.dev/guides/basics/refresh-token-rotation#jwt-strategy
params: {
access_type: "offline",
prompt: "consent",
},
},
// https://github.com/nextauthjs/next-auth/issues/685#issuecomment-785212676
protection: "pkce",
// https://github.com/nextauthjs/next-auth/issues/4707
// @ts-ignore
clientSecret: null,
client: {
token_endpoint_auth_method: "none"
token: {
url: `${OIDC_SERVER_URL}/protocol/openid-connect/token`,
},
userinfo: {
url: `${OIDC_SERVER_URL}/protocol/openid-connect/token`,
},
}),
],
Expand Down
11 changes: 5 additions & 6 deletions pwa/app/bookmarks/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { type Metadata } from "next";
import { redirect } from "next/navigation";

import { List, type Props as ListProps } from "../../components/bookmark/List";
Expand All @@ -10,14 +11,12 @@ interface Query extends URLSearchParams {
page?: number|string|null;
}

export const metadata: Metadata = {
title: 'Bookmarks',
}
async function getServerSideProps({ page = 1 }: Query, session: Session): Promise<ListProps> {
try {
const response: FetchResponse<PagedCollection<Bookmark>> | undefined = await fetchApi(`/bookmarks?page=${Number(page)}`, {
headers: {
// @ts-ignore
Authorization: `Bearer ${session?.accessToken}`,
}
});
const response: FetchResponse<PagedCollection<Bookmark>> | undefined = await fetchApi(`/bookmarks?page=${Number(page)}`, {}, session);
if (!response?.data) {
throw new Error('Unable to retrieve data from /bookmarks.');
}
Expand Down
40 changes: 32 additions & 8 deletions pwa/app/books/[id]/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,59 @@
import { type Metadata } from "next";
import { notFound } from "next/navigation";

import { Show, type Props as ShowProps } from "../../../../components/book/Show";
import { Book } from "../../../../types/Book";
import { type FetchResponse, fetchApi } from "../../../../utils/dataAccess";
import { type Session, auth } from "../../../auth";

interface Query {
id: number;
page?: number|undefined;
interface Props {
params: { id: string };
}

async function getServerSideProps({ id, page = 1 }: Query): Promise<ShowProps|undefined> {
export async function generateMetadata({ params }: Props): Promise<Metadata|undefined> {
const id = params.id;
// @ts-ignore
const session: Session|null = await auth();
try {
const response: FetchResponse<Book> | undefined = await fetchApi(`/books/${id}`, {}, session);
if (!response?.data) {
throw new Error(`Unable to retrieve data from /books/${id}.`);
}
const item = response.data;

return {
title: `${item["title"]}${!!item["author"] && ` - ${item["author"]}`}`,
};
} catch (error) {
console.error(error);
}

return undefined;
}

async function getServerSideProps(id: string, session: Session|null): Promise<ShowProps|undefined> {
try {
const response: FetchResponse<Book> | undefined = await fetchApi(`/books/${id}`, {
headers: {
Preload: "/books/*/reviews",
}
});
}, session);
if (!response?.data) {
throw new Error(`Unable to retrieve data from /books/${id}.`);
}

return { data: response.data, hubURL: response.hubURL, page: Number(page ?? 1) };
return { data: response.data, hubURL: response.hubURL };
} catch (error) {
console.error(error);
}

return undefined;
}

export default async function Page({ searchParams }: { searchParams: Query }) {
const props = await getServerSideProps(searchParams);
export default async function Page({ params }: Props) {
// @ts-ignore
const session: Session|null = await auth();
const props = await getServerSideProps(params.id, session);
if (!props) {
notFound();
}
Expand Down
22 changes: 18 additions & 4 deletions pwa/app/books/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { type Metadata } from "next";

import {auth, type Session} from "../auth";
import { List, type Props as ListProps } from "../../components/book/List";
import { type Book } from "../../types/Book";
import { type PagedCollection } from "../../types/collection";
Expand All @@ -13,7 +16,11 @@ interface Query extends URLSearchParams {
"order[title]"?: string|undefined;
}

async function getServerSideProps(query: Query): Promise<ListProps> {
interface Props {
searchParams: Query;
}

async function getServerSideProps(query: Query, session: Session|null): Promise<ListProps> {
const page = Number(query.page ?? 1);
const filters: FiltersProps = {};
if (query.page) {
Expand All @@ -37,7 +44,9 @@ async function getServerSideProps(query: Query): Promise<ListProps> {
}

try {
const response: FetchResponse<PagedCollection<Book>> | undefined = await fetchApi(buildUriFromFilters("/books", filters));
const response: FetchResponse<PagedCollection<Book>> | undefined = await fetchApi(buildUriFromFilters("/books", filters), {
cache: "force-cache",
}, session);
if (!response?.data) {
throw new Error('Unable to retrieve data from /books.');
}
Expand All @@ -50,8 +59,13 @@ async function getServerSideProps(query: Query): Promise<ListProps> {
return { data: null, hubURL: null, filters, page };
}

export default async function Page({ searchParams }: { searchParams: Query }) {
const props = await getServerSideProps(searchParams);
export const metadata: Metadata = {
title: 'Books Store',
}
export default async function Page({ searchParams }: Props) {
// @ts-ignore
const session: Session|null = await auth();
const props = await getServerSideProps(searchParams, session);

return <List {...props}/>;
}
25 changes: 21 additions & 4 deletions pwa/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
import type { Metadata } from "next";
import { type ReactNode } from "react";
import { SessionProvider } from "next-auth/react";
import "@fontsource/poppins";
import "@fontsource/poppins/600.css";
import "@fontsource/poppins/700.css";

import { Layout } from "../components/common/Layout";
import "../styles/globals.css";
import { Providers } from "./providers";
import { auth } from "./auth";

export const metadata: Metadata = {
title: 'Welcome to API Platform!',
}
export default async function RootLayout({ children }: { children: ReactNode }) {
const session = await auth();

export default async function MyApp({ children }: { children: ReactNode }) {
return (
<Layout>
{children}
</Layout>
<html lang="en">
<body>
<SessionProvider session={session}>
<Providers>
<Layout>
{children}
</Layout>
</Providers>
</SessionProvider>
</body>
</html>
);
};
Loading

0 comments on commit 381a4e3

Please sign in to comment.