Skip to content

Commit

Permalink
Merge pull request #50 from ijsto/SA/Cleanups-+-update-to-KS-v2
Browse files Browse the repository at this point in the history
SA/Cleanups-+-update-to-KS-v2
  • Loading branch information
ScottAgirs authored Jun 15, 2022
2 parents 675362f + 01c8469 commit e0ade35
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 129 deletions.
5 changes: 5 additions & 0 deletions .changeset/orange-walls-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"keystone-6-oauth": minor
---

Updates to Keystone V2
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ jobs:
with:
persist-credentials: false

- name: 🟢 Setup Node.js 14.x
- name: 🟢 Setup Node.js 16.x
uses: actions/setup-node@master
with:
node-version: 14.x
node-version: 16.x

- name: 👩🏼‍🚀 Setup pnpm 6.32.1
uses: pnpm/action-setup@v2.1.0
Expand Down
8 changes: 4 additions & 4 deletions packages/keystone-6-oauth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
"next-auth": "^4.3.1"
},
"devDependencies": {
"@keystone-6/core": "^1.1.0",
"react": "^17.0.2",
"@keystone-6/core": "^2.0.0",
"react": "^18.1.0",
"turbo": "^1.1.6"
},
"peerDependencies": {
"@keystone-6/core": "^1.1.0",
"react": "^17.0.2"
"@keystone-6/core": "^2.0.0",
"react": "^18.1.0"
},
"engines": {
"node": "^14.13 || >= 16.13"
Expand Down
158 changes: 86 additions & 72 deletions packages/keystone-6-oauth/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import url from 'url';
import url from "url";
import {
AdminFileToWrite,
BaseListTypeInfo,
Expand All @@ -7,28 +7,28 @@ import {
AdminUIConfig,
BaseKeystoneTypeInfo,
SessionStrategy,
} from '@keystone-6/core/types';
import { getSession } from 'next-auth/react';
import { getToken } from 'next-auth/jwt';
import { Provider } from 'next-auth/providers';
} from "@keystone-6/core/types";
import { getSession } from "next-auth/react";
import { getToken } from "next-auth/jwt";
import { Provider } from "next-auth/providers";

import * as cookie from 'cookie';
import * as cookie from "cookie";

import { Session } from 'next-auth';
import { nextConfigTemplate } from './templates/next-config';
import { Session } from "next-auth";
import { nextConfigTemplate } from "./templates/next-config";
// import * as Path from 'path';

import { AuthConfig, KeystoneOAuthConfig, AuthSessionStrategy } from './types';
import { getSchemaExtension } from './schema';
import { authTemplate } from './templates/auth';
import { AuthConfig, KeystoneOAuthConfig, AuthSessionStrategy } from "./types";
import { getSchemaExtension } from "./schema";
import { authTemplate } from "./templates/auth";

/**
* createAuth function
*
* Generates config for Keystone to implement standard auth features.
*/

export type { NextAuthProviders, KeystoneOAuthConfig } from './types';
export type { NextAuthProviders, KeystoneOAuthConfig } from "./types";
export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
autoCreate,
cookies,
Expand All @@ -46,7 +46,7 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
// or whether always being true is what we want, in which case we can refactor our code
// to match this. -TL

const customPath = !keystonePath || keystonePath === '/' ? '' : keystonePath;
const customPath = !keystonePath || keystonePath === "/" ? "" : keystonePath;
/**
* pageMiddleware
*
Expand All @@ -57,41 +57,39 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
* - to the init page when initFirstItem is configured, and there are no user in the database
* - to the signin page when no valid session is present
*/
const pageMiddleware: AdminUIConfig<BaseKeystoneTypeInfo>['pageMiddleware'] = async ({
context,
isValidSession,
}) => {
const { req, session } = context;
const pathname = url.parse(req?.url!).pathname!;
const pageMiddleware: AdminUIConfig<BaseKeystoneTypeInfo>["pageMiddleware"] =
async ({ context, isValidSession }) => {
const { req, session } = context;
const pathname = url.parse(req?.url!).pathname!;

if (isValidSession) {
if (isValidSession) {
if (
pathname === `${customPath}/api/auth/signin` ||
(pages?.signIn && pathname.includes(pages?.signIn))
) {
return { kind: "redirect", to: `${customPath}` };
}
if (customPath !== "" && pathname === "/") {
return { kind: "redirect", to: `${customPath}` };
}
return;
}
if (
pathname === `${customPath}/api/auth/signin` ||
(pages?.signIn && pathname.includes(pages?.signIn))
pathname.includes("/_next/") ||
pathname.includes("/api/auth/") ||
(pages?.signIn && pathname.includes(pages?.signIn)) ||
(pages?.error && pathname.includes(pages?.error)) ||
(pages?.signOut && pathname.includes(pages?.signOut))
) {
return { kind: 'redirect', to: `${customPath}` };
return;
}
if (customPath !== '' && pathname === '/') {
return { kind: 'redirect', to: `${customPath}` };
if (!session && !pathname.includes(`${customPath}/api/auth/`)) {
return {
kind: "redirect",
to: pages?.signIn || `${customPath}/api/auth/signin`,
};
}
return;
}
if (
pathname.includes('/_next/') ||
pathname.includes('/api/auth/') ||
(pages?.signIn && pathname.includes(pages?.signIn)) ||
(pages?.error && pathname.includes(pages?.error)) ||
(pages?.signOut && pathname.includes(pages?.signOut))
) {
return;
}
if (!session && !pathname.includes(`${customPath}/api/auth/`)) {
return {
kind: 'redirect',
to: pages?.signIn || `${customPath}/api/auth/signin`,
};
}
};
};

/**
* getAdditionalFiles
Expand All @@ -104,8 +102,8 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
const getAdditionalFiles = () => {
const filesToWrite: AdminFileToWrite[] = [
{
mode: 'write',
outputPath: 'pages/api/auth/[...nextauth].js',
mode: "write",
outputPath: "pages/api/auth/[...nextauth].js",
src: authTemplate({
autoCreate,
identityField,
Expand All @@ -115,8 +113,8 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
}),
},
{
mode: 'write',
outputPath: 'next.config.js',
mode: "write",
outputPath: "next.config.js",
src: nextConfigTemplate({ keystonePath: customPath }),
},
];
Expand Down Expand Up @@ -194,20 +192,18 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
const withItemData = (
_sessionStrategy: AuthSessionStrategy<Record<string, any>>
): AuthSessionStrategy<{ listKey: string; itemId: string; data: any }> => {
const { get, ...sessionStrategy } = _sessionStrategy;
const { get, end, ...sessionStrategy } = _sessionStrategy;
return {
...sessionStrategy,
start: async () => {
return 'false';
},
get: async ({ req, createContext }) => {
const sudoContext = createContext({ sudo: true });
const pathname = url.parse(req?.url!).pathname!;
let nextSession: Session;
if (pathname.includes('/api/auth')) {
if (pathname.includes("/api/auth")) {
return;
}
if (req.headers?.authorization?.split(' ')[0] === 'Bearer') {
const sudoContext = createContext({ sudo: true });

if (req.headers?.authorization?.split(" ")[0] === "Bearer") {
nextSession = (await getToken({
req,
secret: sessionSecret,
Expand All @@ -226,27 +222,38 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
) {
return;
}
const reqWithUser = req as any;
reqWithUser.user = {
istKey: nextSession.listKey,
itemId: nextSession.itemId,
data: nextSession.data,
};

const userSession = await get({ req: reqWithUser, createContext });

return {
...userSession,
...nextSession,
data: nextSession.data,
listKey: nextSession.listKey,
itemId: nextSession.itemId,
};
},
end: async ({ res, req }) => {
end: async ({ res, req, createContext }) => {
await end({ res, req, createContext });
const TOKEN_NAME =
process.env.NODE_ENV === 'production'
? '__Secure-next-auth.session-token'
: 'next-auth.session-token';
process.env.NODE_ENV === "production"
? "__Secure-next-auth.session-token"
: "next-auth.session-token";
res.setHeader(
'Set-Cookie',
cookie.serialize(TOKEN_NAME, '', {
"Set-Cookie",
cookie.serialize(TOKEN_NAME, "", {
maxAge: 0,
expires: new Date(),
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
secure: process.env.NODE_ENV === "production",
path: "/",
sameSite: "lax",
// TODO: Update parse to URL
domain: url.parse(req.url as string).hostname as string,
})
Expand All @@ -272,24 +279,28 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
ui = {
...keystoneConfig.ui,
publicPages: [...(keystoneConfig.ui.publicPages || []), ...publicPages],
getAdditionalFiles: [...(keystoneConfig.ui?.getAdditionalFiles || []), getAdditionalFiles],
pageMiddleware: async args =>
(await pageMiddleware(args)) ?? keystoneConfig?.ui?.pageMiddleware?.(args),
getAdditionalFiles: [
...(keystoneConfig.ui?.getAdditionalFiles || []),
getAdditionalFiles,
],
pageMiddleware: async (args) =>
(await pageMiddleware(args)) ??
keystoneConfig?.ui?.pageMiddleware?.(args),
enableSessionItem: true,
isAccessAllowed: async (context: KeystoneContext) => {
const { req } = context;
const pathname = url.parse(req?.url!).pathname!;

// Allow nextjs scripts and static files to be accessed without auth
if (pathname.includes('/_next/')) {
if (pathname.includes("/_next/")) {
return true;
}

// Allow keystone to access /api/__keystone_api_build for hot reloading
if (
process.env.NODE_ENV !== 'production' &&
process.env.NODE_ENV !== "production" &&
context.req?.url !== undefined &&
new URL(context.req.url, 'http://example.com').pathname ===
new URL(context.req.url, "http://example.com").pathname ===
`${customPath}/api/__keystone_api_build`
) {
return true;
Expand All @@ -302,8 +313,11 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
};
}

if (!keystoneConfig.session) throw new TypeError('Missing .session configuration');
const session = withItemData(keystoneConfig.session) as SessionStrategy<any>;
if (!keystoneConfig.session)
throw new TypeError("Missing .session configuration");
const session = withItemData(
keystoneConfig.session
) as SessionStrategy<any>;

const existingExtendGraphQLSchema = keystoneConfig.extendGraphqlSchema;
return {
Expand All @@ -322,7 +336,7 @@ export function createAuth<GeneratedListTypes extends BaseListTypeInfo>({
generateNodeAPI: true,
},
extendGraphqlSchema: existingExtendGraphQLSchema
? schema => existingExtendGraphQLSchema(extendGraphqlSchema(schema))
? (schema) => existingExtendGraphQLSchema(extendGraphqlSchema(schema))
: extendGraphqlSchema,
};
};
Expand Down
Loading

0 comments on commit e0ade35

Please sign in to comment.