Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
159 changes: 47 additions & 112 deletions integration-tests/testkit/auth.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { z } from 'zod';
import type { InternalApi } from '@hive/server';
import { createTRPCProxyClient, httpLink } from '@trpc/client';
import { ensureEnv } from './env';
import { getServiceHost } from './utils';

const SignUpSignInUserResponseModel = z
Expand All @@ -12,6 +9,8 @@ const SignUpSignInUserResponseModel = z
id: z.string(),
timeJoined: z.number(),
}),
accessToken: z.string(),
refreshToken: z.string(),
})
.refine(response => response.user.emails.length === 1)
.transform(response => ({
Expand All @@ -27,118 +26,54 @@ const signUpUserViaEmail = async (
email: string,
password: string,
): Promise<z.TypeOf<typeof SignUpSignInUserResponseModel>> => {
const apiAddress = await getServiceHost('server', 3001);
try {
const response = await fetch(
`${ensureEnv('SUPERTOKENS_CONNECTION_URI')}/appid-public/public/recipe/signup`,
{
method: 'POST',
headers: {
'content-type': 'application/json; charset=UTF-8',
'api-key': ensureEnv('SUPERTOKENS_API_KEY'),
'cdi-version': '4.0',
},
body: JSON.stringify({
email,
password,
}),
const response = await fetch(`http://${apiAddress}/auth-api/signup`, {
method: 'POST',
headers: {
'content-type': 'application/json; charset=UTF-8',
'st-auth-mode': 'header',
'cf-connecting-ip': Math.random().toString().substring(2),
},
);
const body = await response.text();
body: JSON.stringify({
formFields: [
{
id: 'email',
value: email,
},
{
id: 'password',
value: password,
},
{
id: 'firstName',
value: null,
},
{
id: 'lastName',
value: null,
},
],
}),
});
const body = await response.json();

if (response.status !== 200) {
throw new Error(`Signup failed. ${response.status}.\n ${body}`);
if (response.status !== 200 || body.status !== 'OK') {
throw new Error(`Signup failed. ${response.status}.\n ${JSON.stringify(body)}`);
}

return SignUpSignInUserResponseModel.parse(JSON.parse(body));
return SignUpSignInUserResponseModel.parse({
...body,
accessToken: response.headers.get('st-access-token'),
refreshToken: response.headers.get('st-refresh-token'),
});
} catch (e) {
console.warn(`Failed to sign up:`, e);

throw e;
}
};

const createSessionPayload = (superTokensUserId: string, email: string) => ({
version: '1',
superTokensUserId,
email,
});

const CreateSessionModel = z.object({
accessToken: z.object({
token: z.string(),
}),
refreshToken: z.object({
token: z.string(),
}),
});

const createSession = async (
superTokensUserId: string,
email: string,
oidcIntegrationId: string | null,
) => {
try {
const graphqlAddress = await getServiceHost('server', 8082);

const internalApi = createTRPCProxyClient<InternalApi>({
links: [
httpLink({
url: `http://${graphqlAddress}/trpc`,
fetch,
}),
],
});

await internalApi.ensureUser.mutate({
superTokensUserId,
email,
oidcIntegrationId,
firstName: null,
lastName: null,
});

const sessionData = createSessionPayload(superTokensUserId, email);
const payload = {
enableAntiCsrf: false,
userId: superTokensUserId,
userDataInDatabase: sessionData,
userDataInJWT: sessionData,
};

const response = await fetch(
`${ensureEnv('SUPERTOKENS_CONNECTION_URI')}/appid-public/public/recipe/session`,
{
method: 'POST',
headers: {
'content-type': 'application/json; charset=UTF-8',
'api-key': ensureEnv('SUPERTOKENS_API_KEY'),
rid: 'session',
'cdi-version': '4.0',
},
body: JSON.stringify(payload),
},
);
const body = await response.text();

if (response.status !== 200) {
throw new Error(`Create session failed. ${response.status}.\n ${body}`);
}

const data = CreateSessionModel.parse(JSON.parse(body));

/**
* These are the required cookies that need to be set.
*/
return {
access_token: data.accessToken.token,
refresh_token: data.refreshToken.token,
};
} catch (e) {
console.warn(`Failed to create session:`, e);
throw e;
}
};

const password = 'ilikebigturtlesandicannotlie47';

export function userEmail(userId: string) {
Expand All @@ -149,22 +84,22 @@ const tokenResponsePromise: {
[key: string]: Promise<z.TypeOf<typeof SignUpSignInUserResponseModel>> | null;
} = {};

export function authenticate(
email: string,
): Promise<{ access_token: string; refresh_token: string }>;
export function authenticate(email: string): Promise<{ accessToken: string; refreshToken: string }>;
export function authenticate(
email: string,
oidcIntegrationId?: string,
): Promise<{ access_token: string; refresh_token: string }>;
export function authenticate(
): Promise<{ accessToken: string; refreshToken: string }>;
export async function authenticate(
email: string | string,
oidcIntegrationId?: string,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The type annotation string | string for the email parameter is redundant. It can be simplified to just string for better code clarity and conciseness.

Suggested change
oidcIntegrationId?: string,
email: string,

): Promise<{ access_token: string; refresh_token: string }> {
): Promise<{ accessToken: string; refreshToken: string }> {
if (!tokenResponsePromise[email]) {
tokenResponsePromise[email] = signUpUserViaEmail(email, password);
}

return tokenResponsePromise[email]!.then(data =>
createSession(data.user.id, data.user.email, oidcIntegrationId ?? null),
);
const data = await tokenResponsePromise[email]!;
return {
accessToken: data.accessToken,
refreshToken: data.refreshToken,
};
}
6 changes: 3 additions & 3 deletions integration-tests/testkit/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ export function initSeed() {
async createOwner() {
const ownerEmail = userEmail(generateUnique());
const auth = await authenticate(ownerEmail);
const ownerRefreshToken = auth.refresh_token;
const ownerToken = auth.access_token;
const ownerRefreshToken = auth.refreshToken;
const ownerToken = auth.accessToken;

return {
ownerEmail,
Expand Down Expand Up @@ -895,7 +895,7 @@ export function initSeed() {
resources: GraphQLSchema.ResourceAssignmentInput | undefined = undefined,
) {
const memberEmail = userEmail(generateUnique());
const memberToken = await authenticate(memberEmail).then(r => r.access_token);
const memberToken = await authenticate(memberEmail).then(r => r.accessToken);

const invitationResult = await inviteToOrganization(
{
Expand Down
13 changes: 6 additions & 7 deletions integration-tests/tests/api/oidc-integrations/crud.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ describe('delete', () => {
}
`);

const { access_token: memberAccessToken } = await seed.authenticate(
const { accessToken: memberAccessToken } = await seed.authenticate(
seed.generateEmail(),
oidcIntegrationId,
);
Expand Down Expand Up @@ -805,7 +805,7 @@ describe('restrictions', () => {
}

const nonOidcAccount = await seed.authenticate(userEmail('non-oidc-user'));
const joinResult = await joinMemberUsingCode(invitationCode, nonOidcAccount.access_token).then(
const joinResult = await joinMemberUsingCode(invitationCode, nonOidcAccount.accessToken).then(
r => r.expectNoGraphQLErrors(),
);

Expand Down Expand Up @@ -872,7 +872,7 @@ describe('restrictions', () => {
}

const nonOidcAccount = await seed.authenticate(userEmail('non-oidc-user'));
const joinResult = await joinMemberUsingCode(invitationCode, nonOidcAccount.access_token).then(
const joinResult = await joinMemberUsingCode(invitationCode, nonOidcAccount.accessToken).then(
r => r.expectNoGraphQLErrors(),
);

Expand All @@ -894,10 +894,9 @@ describe('restrictions', () => {
}

const nonOidcAccount = await seed.authenticate(userEmail('non-oidc-user'));
const joinResult = await joinMemberUsingCode(
invitationCode,
nonOidcAccount.access_token,
).then(r => r.expectNoGraphQLErrors());
const joinResult = await joinMemberUsingCode(invitationCode, nonOidcAccount.accessToken).then(
r => r.expectNoGraphQLErrors(),
);

expect(joinResult.joinOrganization.__typename).toEqual('OrganizationPayload');

Expand Down
4 changes: 2 additions & 2 deletions integration-tests/tests/api/organization/members.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ test.concurrent(

// Join
const extra = seed.generateEmail();
const { access_token: member_access_token } = await seed.authenticate(extra);
const { accessToken: member_access_token } = await seed.authenticate(extra);
const joinResult = await (
await joinMemberUsingCode(inviteCode, member_access_token)
).expectNoGraphQLErrors();
Expand All @@ -200,7 +200,7 @@ test.concurrent(
}

const other = seed.generateEmail();
const { access_token: other_access_token } = await seed.authenticate(other);
const { accessToken: other_access_token } = await seed.authenticate(other);
const otherJoinResult = await (
await joinMemberUsingCode(inviteCode, other_access_token)
).expectNoGraphQLErrors();
Expand Down
Loading