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

Pass token description to the API server when creating a new access token #35

Merged
merged 2 commits into from
May 23, 2024
Merged
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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

// Right now, the REST interface spec is always the same version as the galasa framework bundles.
def galasaFrameworkVersion = '0.31.0'
def galasaFrameworkVersion = '0.34.0'
def galasaOpenApiYamlVersion = galasaFrameworkVersion

repositories {
Expand Down
12 changes: 10 additions & 2 deletions galasa-ui/src/app/auth/tokens/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
import { getAuthApiClientWithAuthHeader, sendAuthRequest } from '@/utils/auth';
import AuthCookies from '@/utils/authCookies';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
import { NextRequest, NextResponse } from 'next/server';

// Stop this route from being pre-rendered
export const dynamic = 'force-dynamic';

interface TokenDetails {
tokenDescription: string,
}

// POST request handler for requests to /auth/tokens
export async function POST() {
export async function POST(request: NextRequest) {
// Call out to the API server's /auth/clients endpoint to create a new Dex client
const dexClient = await getAuthApiClientWithAuthHeader().postClients();

Expand All @@ -21,6 +25,10 @@ export async function POST() {
// Store the client ID to be displayed to the user later
cookies().set(AuthCookies.CLIENT_ID, clientId, { httpOnly: true });

// Store the token description to be passed to the API server on the callback
const requestBody: TokenDetails = await request.json();
cookies().set(AuthCookies.TOKEN_DESCRIPTION, requestBody.tokenDescription, { httpOnly: true });

// Authenticate with the created client to get a new refresh token for this client
const authResponse = await sendAuthRequest(clientId);

Expand Down
3 changes: 3 additions & 0 deletions galasa-ui/src/components/TokenRequestModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export default function TokenRequestModal() {
try {
const response = await fetch('/auth/tokens', {
method: 'POST',
body: JSON.stringify({
tokenDescription: tokenNameInputRef.current?.value
}),
});

if (!response.ok) {
Expand Down
2 changes: 1 addition & 1 deletion galasa-ui/src/components/TokenResponseModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function TokenResponseModal({ refreshToken, clientId, onLoad }: T
Copy the following property into the galasactl.properties file in your Galasa home directory* or set it as an environment variable in your
terminal to allow your client tool to access the Galasa Ecosystem.
</p>
<CodeSnippet type="multi">{`GALASA_TOKEN=${token}:${clientIdState}`}</CodeSnippet>
<CodeSnippet type="multi" wrapText>{`GALASA_TOKEN=${token}:${clientIdState}`}</CodeSnippet>
<InlineNotification
title="The personal access token details are not stored and cannot be retrieved when this dialog is closed."
subtitle="Remember to copy the details shown above before closing this dialog."
Expand Down
8 changes: 6 additions & 2 deletions galasa-ui/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@ const handleCallback = async (request: NextRequest, response: NextResponse) => {
clientId = clientIdCookie.value;
}

const tokenDescription = request.cookies.get(AuthCookies.TOKEN_DESCRIPTION)?.value;
response.cookies.delete(AuthCookies.TOKEN_DESCRIPTION);

// Build the request body
const authProperties = buildAuthProperties(clientId, code);
const authProperties = buildAuthProperties(clientId, code, tokenDescription);

// Send a POST request to the API server's /auth endpoint to exchange the authorization code with a JWT
const tokenResponse = await authApiClient.postAuthenticate(authProperties);
Expand All @@ -103,11 +106,12 @@ const handleCallback = async (request: NextRequest, response: NextResponse) => {
return response;
};

const buildAuthProperties = (clientId: string, code: string) => {
const buildAuthProperties = (clientId: string, code: string, tokenDescription?: string) => {
const authProperties = new AuthProperties();

authProperties.clientId = clientId;
authProperties.code = code;
authProperties.description = tokenDescription;

return authProperties;
};
Expand Down
4 changes: 2 additions & 2 deletions galasa-ui/src/tests/__snapshots__/index.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ exports[`renders Galasa Ecosystem homepage 1`] = `
Copy the following property into the galasactl.properties file in your Galasa home directory* or set it as an environment variable in your terminal to allow your client tool to access the Galasa Ecosystem.
</p>
<div
class="cds--snippet cds--snippet--multi"
class="cds--snippet cds--snippet--multi cds--snippet--wraptext"
>
<div
aria-label="Copy to clipboard"
Expand Down Expand Up @@ -573,7 +573,7 @@ exports[`renders Galasa Ecosystem homepage 1`] = `
Copy the following property into the galasactl.properties file in your Galasa home directory* or set it as an environment variable in your terminal to allow your client tool to access the Galasa Ecosystem.
</p>
<div
class="cds--snippet cds--snippet--multi"
class="cds--snippet cds--snippet--multi cds--snippet--wraptext"
>
<div
aria-label="Copy to clipboard"
Expand Down
25 changes: 22 additions & 3 deletions galasa-ui/src/tests/routes/authTokens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
import * as AuthTokenRoute from '@/app/auth/tokens/route';
import { AuthenticationAPIApi } from '@/generated/galasaapi';
import { NextRequest } from 'next/server';

const mockAuthenticationApi = AuthenticationAPIApi as jest.Mock;

Expand Down Expand Up @@ -35,6 +36,12 @@ describe('POST /auth/tokens', () => {
// Given...
const redirectUrl = 'http://my-connector/auth';

const requestBody = JSON.stringify({
tokenDescription: "my-token"
})

const request = new NextRequest("https://my-server/auth/tokens", { method: "POST", body: requestBody })

global.fetch = jest.fn(() =>
Promise.resolve({
url: redirectUrl,
Expand All @@ -45,7 +52,7 @@ describe('POST /auth/tokens', () => {
) as jest.Mock;

// When...
const response = await AuthTokenRoute.POST();
const response = await AuthTokenRoute.POST(request);
const responseJson = await response.json();

// Then...
Expand All @@ -56,6 +63,12 @@ describe('POST /auth/tokens', () => {
// Given...
const redirectUrl = 'http://my-connector/auth';

const requestBody = JSON.stringify({
tokenDescription: "my-token"
})

const request = new NextRequest("https://my-server/auth/tokens", { method: "POST", body: requestBody })

global.fetch = jest.fn(() =>
Promise.resolve({
url: redirectUrl,
Expand All @@ -68,14 +81,20 @@ describe('POST /auth/tokens', () => {
});

// When/Then...
await expect(AuthTokenRoute.POST()).rejects.toMatch(errorMessage);
await expect(AuthTokenRoute.POST(request)).rejects.toMatch(errorMessage);
mockAuthenticationApi.mockReset();
});

it('throws an error if the newly created Dex client does not contain a client ID', async () => {
// Given...
const redirectUrl = 'http://my-connector/auth';

const requestBody = JSON.stringify({
tokenDescription: "my-token"
})

const request = new NextRequest("https://my-server/auth/tokens", { method: "POST", body: requestBody })

global.fetch = jest.fn(() =>
Promise.resolve({
url: redirectUrl,
Expand All @@ -87,7 +106,7 @@ describe('POST /auth/tokens', () => {
});

// When/Then...
await expect(AuthTokenRoute.POST()).rejects.toThrow(/failed to create personal access token/i);
await expect(AuthTokenRoute.POST(request)).rejects.toThrow(/failed to create personal access token/i);
mockAuthenticationApi.mockReset();
});
});
1 change: 1 addition & 0 deletions galasa-ui/src/utils/authCookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const AuthCookies = {
CLIENT_ID: 'client_id',
REFRESH_TOKEN: 'refresh_token',
ID_TOKEN: 'id_token',
TOKEN_DESCRIPTION: 'token_description',
};

export default AuthCookies;
Loading