Skip to content

Commit

Permalink
225/refresh token error (#252)
Browse files Browse the repository at this point in the history
fix: refresh access token
  • Loading branch information
admy7 authored May 15, 2024
1 parent cafdc7c commit 6977f80
Show file tree
Hide file tree
Showing 25 changed files with 166 additions and 145 deletions.
3 changes: 2 additions & 1 deletion src/app/api/applications/[id]/attachments/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { authOptions } from '@/app/api/auth/config';
import { handleErrorResponse } from '@/app/api/errorHandling';
import { addAttachmentToApplication } from '@/services/daam/index.server';
import { ExtendedSession, authOptions } from '@/utils/auth';
import { getServerSession } from 'next-auth';
import { NextResponse } from 'next/server';

Expand Down
3 changes: 2 additions & 1 deletion src/app/api/applications/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
// SPDX-License-Identifier: Apache-2.0

import { retrieveApplication } from '@/services/daam/index.server';
import { ExtendedSession, authOptions } from '@/utils/auth';
import { getServerSession } from 'next-auth';
import { NextResponse } from 'next/server';
import { ExtendedSession } from '../../auth/auth.types';
import { authOptions } from '../../auth/config';
import { handleErrorResponse } from '../../errorHandling';

export async function GET(request: Request, params: { params: { id: string } }) {
Expand Down
3 changes: 2 additions & 1 deletion src/app/api/applications/[id]/save-forms-and-duos/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { authOptions } from '@/app/api/auth/config';
import { handleErrorResponse } from '@/app/api/errorHandling';
import { saveFormAndDuos } from '@/services/daam/index.server';
import { ExtendedSession, authOptions } from '@/utils/auth';
import { getServerSession } from 'next-auth';
import { NextResponse } from 'next/server';

Expand Down
3 changes: 2 additions & 1 deletion src/app/api/applications/[id]/submit/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { authOptions } from '@/app/api/auth/config';
import { handleErrorResponse } from '@/app/api/errorHandling';
import { submitApplication } from '@/services/daam/index.server';
import { ExtendedSession, authOptions } from '@/utils/auth';
import { AxiosResponse } from 'axios';
import { getServerSession } from 'next-auth';
import { NextResponse } from 'next/server';
Expand Down
3 changes: 2 additions & 1 deletion src/app/api/applications/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
// SPDX-License-Identifier: Apache-2.0

import { createApplication, listApplications } from '@/services/daam/index.server';
import { ExtendedSession, authOptions } from '@/utils/auth';
import { getServerSession } from 'next-auth';
import { NextResponse } from 'next/server';
import { ExtendedSession } from '../auth/auth.types';
import { handleErrorResponse } from '../errorHandling';
import { authOptions } from '../auth/config';

export async function POST(request: Request) {
const session: ExtendedSession | null = await getServerSession(authOptions);
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: Apache-2.0

import { authOptions } from '@/utils/auth';
import NextAuth from 'next-auth/next';
import { authOptions } from '../config';

const handler = NextAuth(authOptions);

Expand Down
54 changes: 54 additions & 0 deletions src/app/api/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2024 PNED G.I.E.
//
// SPDX-License-Identifier: Apache-2.0

import { decrypt } from '@/utils/encryption';
import { jwtDecode } from 'jwt-decode';
import { Account, getServerSession } from 'next-auth';
import type { JWT } from 'next-auth/jwt';
import { ExtendedSession } from './auth.types';
import { authOptions } from './config';

export async function getToken(tokenType: 'access_token' | 'id_token') {
const session = (await getServerSession(authOptions)) as ExtendedSession;
if (session) {
const tokenDecrypted = decrypt(session[tokenType]!);
return tokenDecrypted;
}
return null;
}

export function completeTokenWithAccountInfo(token: JWT, account: Account): JWT {
return {
...token,
decoded: jwtDecode(account.access_token!) as string,
access_token: account.access_token as string,
id_token: account.id_token as string,
refresh_token: account.refresh_token as string,
expires_at: account.expires_at as number,
};
}

export async function refreshAccessToken(token: JWT) {
const response = await fetch(`${process.env.REFRESH_TOKEN_URL}`, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: `${process.env.KEYCLOAK_CLIENT_ID}`,
client_secret: `${process.env.KEYCLOAK_CLIENT_SECRET}`,
grant_type: 'refresh_token',
refresh_token: token.refresh_token as string,
}),
method: 'POST',
cache: 'no-cache',
});
const refreshToken = await response.json();

return {
...token,
access_token: refreshToken.access_token,
id_token: refreshToken.id_token,
decoded: jwtDecode(refreshToken.access_token),
expires_at: Math.floor(Date.now() / 1000) + refreshToken.expires_in,
refresh_token: refreshToken.refresh_token,
};
}
18 changes: 18 additions & 0 deletions src/app/api/auth/auth.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2024 PNED G.I.E.
//
// SPDX-License-Identifier: Apache-2.0

import { Account, Session } from 'next-auth';
import { JWT } from 'next-auth/jwt';

export type ExtendedSession = Session & { id_token: string; access_token: string; error?: string };

export type JWTCallbackEntry = {
token: JWT;
account: Account | null;
};

export type SessionCallbackEntry = {
token: JWT;
session: Session;
};
52 changes: 52 additions & 0 deletions src/app/api/auth/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: 2024 PNED G.I.E.
//
// SPDX-License-Identifier: Apache-2.0

import { encrypt } from '@/utils/encryption';
import { keycloackSessionLogOut } from '@/utils/logout';
import type { NextAuthOptions } from 'next-auth';
import Keycloack from 'next-auth/providers/keycloak';
import { signOut } from 'next-auth/react';
import { completeTokenWithAccountInfo, refreshAccessToken } from './auth';
import { JWTCallbackEntry, SessionCallbackEntry } from './auth.types';

export const authOptions: NextAuthOptions = {
providers: [
Keycloack({
clientId: `${process.env.KEYCLOAK_CLIENT_ID}`,
clientSecret: `${process.env.KEYCLOAK_CLIENT_SECRET}`,
issuer: process.env.KEYCLOAK_ISSUER_URL,
authorization: { params: { scope: 'openid profile email elixir_id' } },
}),
],
callbacks: {
async jwt({ token, account }: JWTCallbackEntry) {
const currTimestamp = Math.floor(Date.now() / 1000);
const isTokenExpired = (token?.expires_at as number) < currTimestamp;

if (account) {
return completeTokenWithAccountInfo(token, account);
} else if (isTokenExpired) {
try {
const refreshedToken = await refreshAccessToken(token);
return refreshedToken;
} catch (error) {
keycloackSessionLogOut().then(() => signOut({ callbackUrl: '/' }));
throw new Error('Could not refresh the token. Logging out...');
}
} else {
return token;
}
},

async session({ session, token }: SessionCallbackEntry) {
return {
...session,
access_token: encrypt(token.access_token as string),
id_token: encrypt(token.id_token as string),
roles: (token.decoded as { realm_access?: { roles?: string[] } }).realm_access?.roles,
error: token.error,
};
},
},
};
4 changes: 3 additions & 1 deletion src/app/api/auth/logout/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
//
// SPDX-License-Identifier: Apache-2.0

import { authOptions, getToken } from '@/utils/auth';
import { getServerSession } from 'next-auth';
import { getToken } from '../auth';
import { authOptions } from '../config';

export async function GET() {
const session = await getServerSession(authOptions);
Expand All @@ -15,6 +16,7 @@ export async function GET() {
try {
await fetch(url);
} catch (err) {
console.error(`Could not log out from Keycloak`, err);
return new Response(null, { status: 500 });
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/app/api/datasets/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

import { datasetList } from '@/services/discovery';
import { mapFacetGroups } from '@/services/discovery/utils';
import { ExtendedSession, authOptions } from '@/utils/auth';
import axios from 'axios';
import { getServerSession } from 'next-auth';
import { NextResponse } from 'next/server';
import axios from 'axios';
import { ExtendedSession } from '../auth/auth.types';
import { authOptions } from '../auth/config';

export async function POST(request: Request) {
const session: ExtendedSession | null = await getServerSession(authOptions);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Header/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
DropdownMenuTrigger,
} from "@/components/shadcn/dropdown-menu";
import { User } from "@/types/user.types";
import { keycloackSessionLogOut } from "@/utils/auth";
import { keycloackSessionLogOut } from "@/utils/logout";
import { faSignOut } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { signOut } from "next-auth/react";
Expand Down
2 changes: 1 addition & 1 deletion src/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { SCREEN_SIZE } from "@/hooks/useWindowSize";
import { useDatasetBasket } from "@/providers/DatasetBasketProvider";
import logo from "@/public/egdi-logo-horizontal-full-color-rgb.svg";
import { User } from "@/types/user.types";
import { keycloackSessionLogOut } from "@/utils/auth";
import { keycloackSessionLogOut } from "@/utils/logout";
import {
faBars,
faDatabase,
Expand Down
2 changes: 1 addition & 1 deletion src/services/daam/backend/addAttachmentToApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { AddedAttachment } from '@/types/application.types';
import { ExtendedSession } from '@/utils/auth';
import { decrypt } from '@/utils/encryption';
import axios, { AxiosResponse } from 'axios';

Expand Down
2 changes: 1 addition & 1 deletion src/services/daam/backend/createApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { CreateApplicationResponse } from '@/types/application.types';
import { ExtendedSession } from '@/utils/auth';
import { decrypt } from '@/utils/encryption';
import axios, { AxiosResponse } from 'axios';

Expand Down
2 changes: 1 addition & 1 deletion src/services/daam/backend/listApplications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { ListedApplication } from '@/types/application.types';
import { ExtendedSession } from '@/utils/auth';
import { decrypt } from '@/utils/encryption';
import axios, { AxiosResponse } from 'axios';

Expand Down
2 changes: 1 addition & 1 deletion src/services/daam/backend/retrieveApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { RetrievedApplication } from '@/types/application.types';
import { ExtendedSession } from '@/utils/auth';
import { decrypt } from '@/utils/encryption';
import axios, { AxiosResponse } from 'axios';

Expand Down
2 changes: 1 addition & 1 deletion src/services/daam/backend/saveFormAndDuos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { SaveDUOCode, SaveForm } from '@/types/application.types';
import { ExtendedSession } from '@/utils/auth';
import { decrypt } from '@/utils/encryption';
import axios, { AxiosResponse } from 'axios';

Expand Down
2 changes: 1 addition & 1 deletion src/services/daam/backend/submitApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { ErrorResponse, ValidationWarnings } from '@/types/api.types';
import { ExtendedSession } from '@/utils/auth';
import { decrypt } from '@/utils/encryption';
import axios, { AxiosResponse } from 'axios';

Expand Down
4 changes: 2 additions & 2 deletions src/services/discovery/__tests__/datasetGet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import { encrypt } from '@/utils/encryption';
import { jest } from '@jest/globals';
import axios from 'axios';
import { makeDatasetGet } from '../datasetGet';
import { retrivedDatasetFixture } from '../fixtures/datasetFixtures';
import { ExtendedSession } from '@/utils/auth';
import { encrypt } from '@/utils/encryption';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
Expand Down
2 changes: 1 addition & 1 deletion src/services/discovery/datasetGet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import axios from 'axios';
import { RetrievedDataset } from './types/dataset.types';
import { createHeaders } from './utils';
import { ExtendedSession } from '@/utils/auth';

export const makeDatasetGet = (discoveryUrl: string) => {
return async (id: string, session?: ExtendedSession): Promise<RetrievedDataset> => {
Expand Down
4 changes: 2 additions & 2 deletions src/services/discovery/datasetList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
//
// SPDX-License-Identifier: Apache-2.0

import { ExtendedSession } from '@/app/api/auth/auth.types';
import axios, { AxiosResponse } from 'axios';
import { DatasetSearchQuery, DatasetSearchOptions, DatasetsSearchResponse } from './types/datasetSearch.types';
import { DatasetSearchOptions, DatasetSearchQuery, DatasetsSearchResponse } from './types/datasetSearch.types';
import { createHeaders } from './utils';
import { ExtendedSession } from '@/utils/auth';

export const makeDatasetList = (discoveryUrl: string) => {
return async (options: DatasetSearchOptions, session?: ExtendedSession): Promise<AxiosResponse<DatasetsSearchResponse>> => {
Expand Down
2 changes: 1 addition & 1 deletion src/services/discovery/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2024 PNED G.I.E.
//
// SPDX-License-Identifier: Apache-2.0
import { ExtendedSession } from '@/utils/auth';
import { ExtendedSession } from '@/app/api/auth/auth.types';
import { decrypt } from '@/utils/encryption';
import { DatasetSearchQuery, FacetGroup, facetToLabelMapping } from './types/datasetSearch.types';

Expand Down
Loading

0 comments on commit 6977f80

Please sign in to comment.