Skip to content

Commit

Permalink
update to be a separate use case
Browse files Browse the repository at this point in the history
  • Loading branch information
Roystbeef committed Nov 1, 2023
1 parent f158bad commit 4bcd672
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 94 deletions.
26 changes: 7 additions & 19 deletions admin/src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,15 @@ export type FigmaUser = {
readonly email: string;
};

export type CheckAuthResponseBody =
| {
readonly type: '3LO';
readonly authorized: true;
readonly grant: {
readonly authorizationEndpoint: string;
};
readonly user: FigmaUser;
}
| {
readonly type: '3LO';
readonly authorized: false;
readonly grant: {
readonly authorizationEndpoint: string;
};
};
export type MeResponseBody = {
readonly authorizationEndpoint: string;
readonly user?: FigmaUser;
};

export async function checkAuth(
export async function getAuthMe(
atlassianUserId: string,
): Promise<AxiosResponse<CheckAuthResponseBody>> {
return await axiosRest.get<CheckAuthResponseBody>('/admin/auth/checkAuth', {
): Promise<AxiosResponse<MeResponseBody>> {
return await axiosRest.get<MeResponseBody>('/admin/auth/me', {
params: { userId: atlassianUserId },
});
}
26 changes: 11 additions & 15 deletions admin/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Spinner from '@atlaskit/spinner';
import { useQuery } from '@tanstack/react-query';

import { checkAuth, getAtlassianAccountId } from './api';
import { getAtlassianAccountId, getAuthMe } from './api';
import { Page } from './components';
import { AuthPage, TeamsPage } from './pages';

Expand All @@ -13,15 +13,15 @@ export function App() {

const atlassianAccountId = atlassianAccountIdQuery.data;

const checkAuthQuery = useQuery({
queryKey: ['checkAuth', atlassianAccountId],
const getAuthMeQuery = useQuery({
queryKey: ['authMe', atlassianAccountId],
queryFn: async () => {
return (await checkAuth(atlassianAccountId ?? '')).data;
return (await getAuthMe(atlassianAccountId ?? '')).data;
},
enabled: atlassianAccountId != null,
});

if (atlassianAccountIdQuery.isPending || checkAuthQuery.isPending) {
if (atlassianAccountIdQuery.isPending || getAuthMeQuery.isPending) {
return (
<Page>
<Spinner size="large" />
Expand All @@ -34,25 +34,21 @@ export function App() {
return null;
}

if (checkAuthQuery.isError) {
if (getAuthMeQuery.isError) {
// TODO: render an error screen when our service is down or unavailable
return null;
}

const checkAuthResponse = checkAuthQuery.data;
const { authorizationEndpoint, user } = getAuthMeQuery.data;

if (!checkAuthResponse.authorized) {
return (
<AuthPage
authorizationEndpoint={checkAuthResponse.grant.authorizationEndpoint}
/>
);
if (!user) {
return <AuthPage authorizationEndpoint={authorizationEndpoint} />;
}

return (
<TeamsPage
authorizationEndpoint={checkAuthResponse.grant.authorizationEndpoint}
currentUser={checkAuthResponse.user}
authorizationEndpoint={authorizationEndpoint}
currentUser={user}
/>
);
}
15 changes: 15 additions & 0 deletions src/usecases/check-user-figma-auth-use-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ConnectInstallation } from '../domain/entities';
import { figmaService } from '../infrastructure/figma';
export const checkUserFigmaAuthUseCase = {
execute: async (
atlassianUserId: string,
connectInstallation: ConnectInstallation,
): Promise<boolean> => {
const currentUser = await figmaService.fetchCurrentUser({
atlassianUserId,
connectInstallationId: connectInstallation.id,
});

return currentUser != null;
},
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ConnectInstallation, FigmaUser } from '../domain/entities';
import { figmaService } from '../infrastructure/figma';

export const currentFigmaUserUseCase = {
export const getCurrentFigmaUserUseCase = {
execute: async (
atlassianUserId: string,
connectInstallation: ConnectInstallation,
Expand Down
3 changes: 2 additions & 1 deletion src/usecases/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export * from './current-figma-user-use-case';
export * from './associate-entity-use-case';
export * from './check-user-figma-auth-use-case';
export * from './connect-figma-team-use-case';
export * from './disassociate-entity-use-case';
export * from './disconnect-figma-team-use-case';
export * from './errors';
export * from './get-current-figma-user-use-case';
export * from './handle-figma-authorization-response-use-case';
export * from './handle-figma-file-update-event-use-case';
export * from './installed-use-case';
Expand Down
16 changes: 7 additions & 9 deletions src/web/routes/admin/auth/auth-router.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import type { NextFunction } from 'express';
import { Router } from 'express';

import type { CheckAuthRequest, CheckAuthResponse } from './types';
import type { MeRequest, MeResponse } from './types';

import { figmaAuthService } from '../../../../infrastructure/figma';
import { currentFigmaUserUseCase } from '../../../../usecases';
import { getCurrentFigmaUserUseCase } from '../../../../usecases';

export const authRouter = Router();

/**
* Checks whether the given Atlassian admin is authorized to call Figma API.
*/
authRouter.get(
['/checkAuth'],
function (req: CheckAuthRequest, res: CheckAuthResponse, next: NextFunction) {
['/me'],
function (req: MeRequest, res: MeResponse, next: NextFunction) {
const { connectInstallation, atlassianUserId } = res.locals;

currentFigmaUserUseCase
getCurrentFigmaUserUseCase
.execute(atlassianUserId, connectInstallation)
.then((currentUser) => {
const authorizationEndpoint =
Expand All @@ -28,15 +28,13 @@ authRouter.get(

if (currentUser) {
return res.send({
authorized: true,
user: { email: currentUser.email },
grant: { authorizationEndpoint },
authorizationEndpoint,
});
}

return res.send({
authorized: false,
grant: { authorizationEndpoint },
authorizationEndpoint,
});
})
.catch((error) => next(error));
Expand Down
45 changes: 15 additions & 30 deletions src/web/routes/admin/auth/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ const FIGMA_OAUTH_API_BASE_URL =
getConfig().figma.oauth2.authorizationServerBaseUrl;

const FIGMA_OAUTH_REFRESH_TOKEN_ENDPOINT = '/api/oauth/refresh';
const CHECK_AUTH_ENDPOINT = '/admin/auth/checkAuth';
const AUTH_ME_ENDPOINT = '/admin/auth/me';

describe('/admin/auth', () => {
describe('/checkAuth', () => {
describe('/me', () => {
const REFRESH_TOKEN = uuidv4();
const REFRESH_TOKEN_QUERY_PARAMS = generateRefreshOAuth2TokenQueryParams({
client_id: getConfig().figma.oauth2.clientId,
Expand Down Expand Up @@ -69,16 +69,13 @@ describe('/admin/auth', () => {
mockFigmaMeEndpoint({ baseUrl: getConfig().figma.apiBaseUrl });

return request(app)
.get(CHECK_AUTH_ENDPOINT)
.get(AUTH_ME_ENDPOINT)
.set('Authorization', `JWT ${jwt}`)
.expect(HttpStatusCode.Ok)
.then((response) => {
expect(response.body).toStrictEqual({
authorized: true,
user: { email: expect.any(String) },
grant: {
authorizationEndpoint: expect.any(String),
},
authorizationEndpoint: expect.any(String),
});
});
});
Expand Down Expand Up @@ -118,16 +115,13 @@ describe('/admin/auth', () => {
mockFigmaMeEndpoint({ baseUrl: getConfig().figma.apiBaseUrl });

await request(app)
.get(CHECK_AUTH_ENDPOINT)
.get(AUTH_ME_ENDPOINT)
.set('Authorization', `JWT ${jwt}`)
.expect(HttpStatusCode.Ok)
.then((response) => {
expect(response.body).toStrictEqual({
authorized: true,
user: { email: expect.any(String) },
grant: {
authorizationEndpoint: expect.any(String),
},
authorizationEndpoint: expect.any(String),
});
});

Expand Down Expand Up @@ -163,15 +157,12 @@ describe('/admin/auth', () => {
});

return request(app)
.get(CHECK_AUTH_ENDPOINT)
.get(AUTH_ME_ENDPOINT)
.set('Authorization', `JWT ${jwt}`)
.expect(HttpStatusCode.Ok)
.then((response) => {
expect(response.body).toStrictEqual({
authorized: false,
grant: {
authorizationEndpoint: expect.any(String),
},
authorizationEndpoint: expect.any(String),
});
});
});
Expand Down Expand Up @@ -209,15 +200,12 @@ describe('/admin/auth', () => {
.reply(HttpStatusCode.InternalServerError);

return request(app)
.get(CHECK_AUTH_ENDPOINT)
.get(AUTH_ME_ENDPOINT)
.set('Authorization', `JWT ${jwt}`)
.expect(HttpStatusCode.Ok)
.then((response) => {
expect(response.body).toStrictEqual({
authorized: false,
grant: {
authorizationEndpoint: expect.any(String),
},
authorizationEndpoint: expect.any(String),
});
});
});
Expand Down Expand Up @@ -254,15 +242,12 @@ describe('/admin/auth', () => {
});

return request(app)
.get(CHECK_AUTH_ENDPOINT)
.get(AUTH_ME_ENDPOINT)
.set('Authorization', `JWT ${jwt}`)
.expect(HttpStatusCode.Ok)
.then((response) => {
expect(response.body).toStrictEqual({
authorized: false,
grant: {
authorizationEndpoint: expect.any(String),
},
authorizationEndpoint: expect.any(String),
});
});
});
Expand All @@ -289,7 +274,7 @@ describe('/admin/auth', () => {
});

return request(app)
.get(CHECK_AUTH_ENDPOINT)
.get(AUTH_ME_ENDPOINT)
.query({
userId: atlassianUserId,
})
Expand All @@ -298,7 +283,7 @@ describe('/admin/auth', () => {
.then((response) => {
const authorizationEndpoint = new URL(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
response.body.grant.authorizationEndpoint,
response.body.authorizationEndpoint,
);

expect(authorizationEndpoint.origin).toBe(
Expand Down Expand Up @@ -354,7 +339,7 @@ describe('/admin/auth', () => {
mockFigmaMeEndpoint({ baseUrl: getConfig().figma.apiBaseUrl });

return request(app)
.get(CHECK_AUTH_ENDPOINT)
.get(AUTH_ME_ENDPOINT)
.set('Authorization', `JWT ${jwt}`)
.expect(HttpStatusCode.Unauthorized);
});
Expand Down
24 changes: 9 additions & 15 deletions src/web/routes/admin/auth/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,26 @@ import type { Request, Response } from 'express';

import type { ConnectInstallation } from '../../../../domain/entities';

export type CheckAuthQueryParameters = { readonly userId: string };
export type MeQueryParameters = { readonly userId: string };

export type CheckAuthResponseBody = {
readonly authorized: boolean;
readonly grant: {
readonly authorizationEndpoint: string;
};
export type MeResponseBody = {
readonly user?: {
readonly email: string;
};
readonly authorizationEndpoint: string;
};

type CheckAuthRequestLocals = {
type MeRequestLocals = {
readonly connectInstallation: ConnectInstallation;
readonly atlassianUserId: string;
};

export type CheckAuthRequest = Request<
export type MeRequest = Request<
Record<string, never>,
CheckAuthResponseBody,
MeResponseBody,
never,
CheckAuthQueryParameters,
CheckAuthRequestLocals
MeQueryParameters,
MeRequestLocals
>;

export type CheckAuthResponse = Response<
CheckAuthResponseBody,
CheckAuthRequestLocals
>;
export type MeResponse = Response<MeResponseBody, MeRequestLocals>;
7 changes: 3 additions & 4 deletions src/web/routes/auth/auth-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CHECK_AUTH_REQUEST_SCHEMA } from './schemas';
import type { CheckAuthRequest, CheckAuthResponse } from './types';

import { figmaAuthService } from '../../../infrastructure/figma';
import { currentFigmaUserUseCase } from '../../../usecases';
import { checkUserFigmaAuthUseCase } from '../../../usecases';
import { requestSchemaValidationMiddleware } from '../../middleware';
import { jiraServerSymmetricJwtAuthMiddleware } from '../../middleware/jira';

Expand All @@ -23,10 +23,9 @@ authRouter.get(
const { connectInstallation } = res.locals;
const atlassianUserId = req.query.userId;

currentFigmaUserUseCase
checkUserFigmaAuthUseCase
.execute(atlassianUserId, connectInstallation)
.then((currentUser) => {
const authorized = currentUser != null;
.then((authorized) => {
if (authorized) {
return res.send({ type: '3LO', authorized });
}
Expand Down

0 comments on commit 4bcd672

Please sign in to comment.