Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(space-nuxt-base): upgrade app-extension-auth #67

Merged
merged 7 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion space-plugins/nuxt-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"postinstall": "nuxt prepare"
},
"dependencies": {
"@storyblok/app-extension-auth": "1.0.3",
"@storyblok/app-extension-auth": "2.0.0-beta.0",
"@storyblok/region-helper": "^1.1.0"
},
"devDependencies": {
Expand Down
48 changes: 21 additions & 27 deletions space-plugins/nuxt-base/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 0 additions & 11 deletions space-plugins/nuxt-base/server/middleware/02.auth.global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,6 @@ export default defineEventHandler(async (event) => {
return;
}

// If the user hasn't been authenticated yet.
// (Storyfront attaches this query parameter in that case)
const queryParams = getQuery(event);
if (queryParams['init_oauth'] === 'true') {
return await sendRedirect(
event,
`${appConfig.auth.initOauthFlowUrl}?init_oauth=true`,
302,
);
}

const appSession = await getAppSession(event);

if (!appSession) {
Expand Down
105 changes: 16 additions & 89 deletions space-plugins/nuxt-base/server/utils/getAppSession.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import {
isAppSessionQuery,
sessionCookieStore,
inferSessionQuery,
getSessionStore,
} from '@storyblok/app-extension-auth';

import type { H3Event } from 'h3';

type AppSessionQuery = {
spaceId: number;
userId: number;
};

// Overall auth flow
// (1) https://app.storyblok.com/v1/spaces/xxxx/app_provisions/xxxx
// (2) https://<USER-DOMAIN>/?space_id=xxxx&space_is_trial=false&space_name=xxxx&user_id=xxxx&user_name=null&user_lang=en&user_is_admin=true
Expand All @@ -29,93 +24,25 @@ type AppSessionQuery = {
// (11) https://<USER-DOMAIN>/api/connect/callback?code=xxxx&state=xxxx&space_id=xxxx

// Finished. Authenticated successfully.
// (12) https://<USER-DOMAIN>/?spaceId=xxxx&userId=xxxx

// If a user visits this plugin with the `sb.auth` token already set, they will be redirected to ↓
// (13) https://<USER-DOMAIN>/?space_id=xxxx&user_id=xxxx
// instead of spaceId and userId.
// It should be more consistent, but at least the `nuxt-base` layer takes care of it now.
// (12) https://<USER-DOMAIN>/?space_id=xxxx&user_id=xxxx

export const getAppSession = async (event: H3Event) => {
const appSessionQuery = extractAppSessionQuery(event);
if (!isAppSessionQuery(appSessionQuery)) {
return;
}

const appConfig = useAppConfig();
const sessionStore = sessionCookieStore(getAuthHandlerParams(appConfig.auth))(
{
req: event.node.req,
res: event.node.res,
},
);

return await sessionStore.get(appSessionQuery);
};

function extractAppSessionQuery(event: H3Event): AppSessionQuery | undefined {
const sessionStore = getSessionStore(getAuthHandlerParams(appConfig.auth))({
req: event.node.req,
res: event.node.res,
});
const appSession = event.context.appSession;
const query = getQuery(event);

if (appSession?.spaceId && appSession?.userId) {
// When a page is already authenticated on the server side,
// and it's rendering a page that includes `useFetch('...', { server: true })`,
// Nuxt directly runs the serverless function on the server side
// (without an actual HTTP request).
//
// In that case `event.context.appSession` exists.
return convertToAppSessionQuery(appSession?.spaceId, appSession?.userId);
if (appSession && appSession.spaceId && appSession.userId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can even expose isAppSession validation method which we could use here?

Copy link
Contributor Author

@eunjae-lee eunjae-lee Jul 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good point.

1d84ff2

return await sessionStore.get({
spaceId: appSession.spaceId,
userId: appSession.userId,
});
}

// (12) if this is a page request (/?spaceId=xxx&userId=yyy)
if (query.spaceId && query.userId) {
return convertToAppSessionQuery(query.spaceId, query.userId);
}

// (13) if this is a page request (/?space_id=xxx&user_id=yyy)
if (query.space_id && query.user_id) {
return convertToAppSessionQuery(query.space_id, query.user_id);
}

const referer = getHeader(event, 'referer');
// if this is an API request (/api/xxx), the `referer` header exists.
if (referer) {
// `referer` can be one of the following:
// https://<USER-DOMAIN>/?spaceId=xxxx&userId=xxxx
// or
// https://<USER-DOMAIN>/?space_id=xxxx&user_id=xxxx
const refererParams = new URL(referer).searchParams;
if (refererParams.get('spaceId') && refererParams.get('userId')) {
return convertToAppSessionQuery(
refererParams.get('spaceId'),
refererParams.get('userId'),
);
}

if (refererParams.get('space_id') && refererParams.get('user_id')) {
return convertToAppSessionQuery(
refererParams.get('space_id'),
refererParams.get('user_id'),
);
}
}
}

function convertToAppSessionQuery(spaceId: any, userId: any): AppSessionQuery {
return {
spaceId: toNumber(spaceId),
userId: toNumber(userId),
};
}

const toNumber = (value: any) => {
if (typeof value === 'string') {
return parseInt(value, 10);
}
if (typeof value === 'number') {
return value;
const appSessionQuery = inferSessionQuery(event.node.req);
if (!appSessionQuery) {
return;
}
throw new Error(
`Expected to be string or number. Actual value: ${JSON.stringify(value)}`,
);
return await sessionStore.get(appSessionQuery);
};