mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-09-20 07:57:29 +03:00
fix: get auth token for development (#4295)
This commit is contained in:
parent
98429bf89e
commit
fc76163dd1
@ -178,11 +178,11 @@ const OpenOAuthJwt = () => {
|
|||||||
}, [params]);
|
}, [params]);
|
||||||
const channel = schemaToChanel[schema as Schema];
|
const channel = schemaToChanel[schema as Schema];
|
||||||
|
|
||||||
if (!currentUser || !currentUser?.token?.token) {
|
if (!currentUser || !currentUser?.token?.sessionToken) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlToOpen = `${schema}://oauth-jwt?token=${currentUser.token.token}`;
|
const urlToOpen = `${schema}://oauth-jwt?token=${currentUser.token.sessionToken}`;
|
||||||
|
|
||||||
return <OpenAppImpl urlToOpen={urlToOpen} channel={channel} />;
|
return <OpenAppImpl urlToOpen={urlToOpen} channel={channel} />;
|
||||||
};
|
};
|
||||||
|
@ -30,6 +30,9 @@ export class TokenType {
|
|||||||
|
|
||||||
@Field()
|
@Field()
|
||||||
refresh!: string;
|
refresh!: string;
|
||||||
|
|
||||||
|
@Field({ nullable: true })
|
||||||
|
sessionToken?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,18 +52,32 @@ export class AuthResolver {
|
|||||||
|
|
||||||
@Throttle(20, 60)
|
@Throttle(20, 60)
|
||||||
@ResolveField(() => TokenType)
|
@ResolveField(() => TokenType)
|
||||||
async token(@CurrentUser() currentUser: UserType, @Parent() user: UserType) {
|
async token(
|
||||||
|
@Context() ctx: { req: Request },
|
||||||
|
@CurrentUser() currentUser: UserType,
|
||||||
|
@Parent() user: UserType
|
||||||
|
) {
|
||||||
if (user.id !== currentUser.id) {
|
if (user.id !== currentUser.id) {
|
||||||
throw new BadRequestException('Invalid user');
|
throw new BadRequestException('Invalid user');
|
||||||
}
|
}
|
||||||
|
|
||||||
// on production we use session token that is stored in database (strategy = 'database')
|
let sessionToken: string | undefined;
|
||||||
const sessionToken = this.config.node.prod
|
|
||||||
? await this.auth.getSessionToken(user.id)
|
// only return session if the request is from the same origin & path == /open-app
|
||||||
: this.auth.sign(user);
|
if (
|
||||||
|
ctx.req.headers.referer &&
|
||||||
|
ctx.req.headers.host &&
|
||||||
|
new URL(ctx.req.headers.referer).pathname.startsWith('/open-app') &&
|
||||||
|
ctx.req.headers.host === new URL(this.config.origin).host
|
||||||
|
) {
|
||||||
|
const cookiePrefix = this.config.node.prod ? '__Secure-' : '';
|
||||||
|
const sessionCookieName = `${cookiePrefix}next-auth.session-token`;
|
||||||
|
sessionToken = ctx.req.cookies?.[sessionCookieName];
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token: sessionToken,
|
sessionToken,
|
||||||
|
token: this.auth.sign(user),
|
||||||
refresh: this.auth.refresh(user),
|
refresh: this.auth.refresh(user),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -251,17 +251,4 @@ export class AuthService {
|
|||||||
async sendChangeEmail(email: string, callbackUrl: string) {
|
async sendChangeEmail(email: string, callbackUrl: string) {
|
||||||
return this.mailer.sendChangeEmail(email, callbackUrl);
|
return this.mailer.sendChangeEmail(email, callbackUrl);
|
||||||
}
|
}
|
||||||
async getSessionToken(userId: string) {
|
|
||||||
const session = await this.prisma.session.findFirst({
|
|
||||||
where: {
|
|
||||||
userId: userId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!session) {
|
|
||||||
throw new BadRequestException(`No session found for user id ${userId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return session?.sessionToken;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ enum NewFeaturesKind {
|
|||||||
type TokenType {
|
type TokenType {
|
||||||
token: String!
|
token: String!
|
||||||
refresh: String!
|
refresh: String!
|
||||||
|
sessionToken: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteUserType {
|
type InviteUserType {
|
||||||
|
@ -7,11 +7,13 @@ import { ConfigModule } from '../config';
|
|||||||
import { GqlModule } from '../graphql.module';
|
import { GqlModule } from '../graphql.module';
|
||||||
import { MetricsModule } from '../metrics';
|
import { MetricsModule } from '../metrics';
|
||||||
import { AuthModule } from '../modules/auth';
|
import { AuthModule } from '../modules/auth';
|
||||||
|
import { AuthResolver } from '../modules/auth/resolver';
|
||||||
import { AuthService } from '../modules/auth/service';
|
import { AuthService } from '../modules/auth/service';
|
||||||
import { PrismaModule } from '../prisma';
|
import { PrismaModule } from '../prisma';
|
||||||
import { RateLimiterModule } from '../throttler';
|
import { RateLimiterModule } from '../throttler';
|
||||||
|
|
||||||
let auth: AuthService;
|
let authService: AuthService;
|
||||||
|
let authResolver: AuthResolver;
|
||||||
let module: TestingModule;
|
let module: TestingModule;
|
||||||
|
|
||||||
// cleanup database before each test
|
// cleanup database before each test
|
||||||
@ -31,6 +33,8 @@ test.beforeEach(async () => {
|
|||||||
refreshTokenExpiresIn: 1,
|
refreshTokenExpiresIn: 1,
|
||||||
leeway: 1,
|
leeway: 1,
|
||||||
},
|
},
|
||||||
|
host: 'example.org',
|
||||||
|
https: true,
|
||||||
}),
|
}),
|
||||||
PrismaModule,
|
PrismaModule,
|
||||||
GqlModule,
|
GqlModule,
|
||||||
@ -39,7 +43,8 @@ test.beforeEach(async () => {
|
|||||||
RateLimiterModule,
|
RateLimiterModule,
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
auth = module.get(AuthService);
|
authService = module.get(AuthService);
|
||||||
|
authResolver = module.get(AuthResolver);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.afterEach.always(async () => {
|
test.afterEach.always(async () => {
|
||||||
@ -47,14 +52,14 @@ test.afterEach.always(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should be able to register and signIn', async t => {
|
test('should be able to register and signIn', async t => {
|
||||||
await auth.signUp('Alex Yang', 'alexyang@example.org', '123456');
|
await authService.signUp('Alex Yang', 'alexyang@example.org', '123456');
|
||||||
await auth.signIn('alexyang@example.org', '123456');
|
await authService.signIn('alexyang@example.org', '123456');
|
||||||
t.pass();
|
t.pass();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should be able to verify', async t => {
|
test('should be able to verify', async t => {
|
||||||
await auth.signUp('Alex Yang', 'alexyang@example.org', '123456');
|
await authService.signUp('Alex Yang', 'alexyang@example.org', '123456');
|
||||||
await auth.signIn('alexyang@example.org', '123456');
|
await authService.signIn('alexyang@example.org', '123456');
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
@ -66,8 +71,8 @@ test('should be able to verify', async t => {
|
|||||||
avatarUrl: '',
|
avatarUrl: '',
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
const token = await auth.sign(user);
|
const token = await authService.sign(user);
|
||||||
const claim = await auth.verify(token);
|
const claim = await authService.verify(token);
|
||||||
t.is(claim.id, '1');
|
t.is(claim.id, '1');
|
||||||
t.is(claim.name, 'Alex Yang');
|
t.is(claim.name, 'Alex Yang');
|
||||||
t.is(claim.email, 'alexyang@example.org');
|
t.is(claim.email, 'alexyang@example.org');
|
||||||
@ -75,8 +80,8 @@ test('should be able to verify', async t => {
|
|||||||
t.is(claim.createdAt.toISOString(), date.toISOString());
|
t.is(claim.createdAt.toISOString(), date.toISOString());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const token = await auth.refresh(user);
|
const token = await authService.refresh(user);
|
||||||
const claim = await auth.verify(token);
|
const claim = await authService.verify(token);
|
||||||
t.is(claim.id, '1');
|
t.is(claim.id, '1');
|
||||||
t.is(claim.name, 'Alex Yang');
|
t.is(claim.name, 'Alex Yang');
|
||||||
t.is(claim.email, 'alexyang@example.org');
|
t.is(claim.email, 'alexyang@example.org');
|
||||||
@ -84,3 +89,90 @@ test('should be able to verify', async t => {
|
|||||||
t.is(claim.createdAt.toISOString(), date.toISOString());
|
t.is(claim.createdAt.toISOString(), date.toISOString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should not be able to return token if user is invalid', async t => {
|
||||||
|
const date = new Date();
|
||||||
|
const user = {
|
||||||
|
id: '1',
|
||||||
|
name: 'Alex Yang',
|
||||||
|
email: 'alexyang@example.org',
|
||||||
|
emailVerified: date,
|
||||||
|
createdAt: date,
|
||||||
|
avatarUrl: '',
|
||||||
|
};
|
||||||
|
const anotherUser = {
|
||||||
|
id: '2',
|
||||||
|
name: 'Alex Yang 2',
|
||||||
|
email: 'alexyang@example.org',
|
||||||
|
emailVerified: date,
|
||||||
|
createdAt: date,
|
||||||
|
avatarUrl: '',
|
||||||
|
};
|
||||||
|
await t.throwsAsync(
|
||||||
|
authResolver.token(
|
||||||
|
{
|
||||||
|
req: {
|
||||||
|
headers: {
|
||||||
|
referer: 'https://example.org',
|
||||||
|
host: 'example.org',
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
},
|
||||||
|
user,
|
||||||
|
anotherUser
|
||||||
|
),
|
||||||
|
{
|
||||||
|
message: 'Invalid user',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not return sessionToken if request headers is invalid', async t => {
|
||||||
|
const date = new Date();
|
||||||
|
const user = {
|
||||||
|
id: '1',
|
||||||
|
name: 'Alex Yang',
|
||||||
|
email: 'alexyang@example.org',
|
||||||
|
emailVerified: date,
|
||||||
|
createdAt: date,
|
||||||
|
avatarUrl: '',
|
||||||
|
};
|
||||||
|
const result = await authResolver.token(
|
||||||
|
{
|
||||||
|
req: {
|
||||||
|
headers: {},
|
||||||
|
} as any,
|
||||||
|
},
|
||||||
|
user,
|
||||||
|
user
|
||||||
|
);
|
||||||
|
t.is(result.sessionToken, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return valid sessionToken if request headers valid', async t => {
|
||||||
|
const date = new Date();
|
||||||
|
const user = {
|
||||||
|
id: '1',
|
||||||
|
name: 'Alex Yang',
|
||||||
|
email: 'alexyang@example.org',
|
||||||
|
emailVerified: date,
|
||||||
|
createdAt: date,
|
||||||
|
avatarUrl: '',
|
||||||
|
};
|
||||||
|
const result = await authResolver.token(
|
||||||
|
{
|
||||||
|
req: {
|
||||||
|
headers: {
|
||||||
|
referer: 'https://example.org/open-app/test',
|
||||||
|
host: 'example.org',
|
||||||
|
},
|
||||||
|
cookies: {
|
||||||
|
'next-auth.session-token': '123456',
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
},
|
||||||
|
user,
|
||||||
|
user
|
||||||
|
);
|
||||||
|
t.is(result.sessionToken, '123456');
|
||||||
|
});
|
||||||
|
@ -7,7 +7,7 @@ query getCurrentUser {
|
|||||||
avatarUrl
|
avatarUrl
|
||||||
createdAt
|
createdAt
|
||||||
token {
|
token {
|
||||||
token
|
sessionToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ query getCurrentUser {
|
|||||||
avatarUrl
|
avatarUrl
|
||||||
createdAt
|
createdAt
|
||||||
token {
|
token {
|
||||||
token
|
sessionToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
|
@ -173,7 +173,7 @@ export type GetCurrentUserQuery = {
|
|||||||
emailVerified: string | null;
|
emailVerified: string | null;
|
||||||
avatarUrl: string | null;
|
avatarUrl: string | null;
|
||||||
createdAt: string | null;
|
createdAt: string | null;
|
||||||
token: { __typename?: 'TokenType'; token: string };
|
token: { __typename?: 'TokenType'; sessionToken: string | null };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user