Skip to content

Commit 57f59c6

Browse files
committed
Make authorizationUrl and tokenUrl mandatory
1 parent 96f3d81 commit 57f59c6

File tree

4 files changed

+36
-83
lines changed

4 files changed

+36
-83
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/server/auth/handlers/token.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ describe('Token Handler', () => {
298298

299299
const proxyProvider = new ProxyOAuthServerProvider({
300300
endpoints: {
301+
authorizationUrl: 'https://example.com/authorize',
301302
tokenUrl: 'https://example.com/token'
302303
},
303304
verifyAccessToken: async (token) => ({

src/server/auth/proxyProvider.test.ts

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe("Proxy OAuth Server Provider", () => {
2222
// Mock provider functions
2323
const mockVerifyToken = jest.fn();
2424
const mockGetClient = jest.fn();
25-
25+
2626
// Base provider options
2727
const baseOptions: ProxyOptions = {
2828
endpoints: {
@@ -66,7 +66,7 @@ describe("Proxy OAuth Server Provider", () => {
6666

6767
// Add helper function for failed responses
6868
const mockFailedResponse = () => {
69-
(global.fetch as jest.Mock).mockImplementation(() =>
69+
(global.fetch as jest.Mock).mockImplementation(() =>
7070
Promise.resolve({
7171
ok: false,
7272
status: 400,
@@ -103,20 +103,6 @@ describe("Proxy OAuth Server Provider", () => {
103103

104104
expect(mockResponse.redirect).toHaveBeenCalledWith(expectedUrl.toString());
105105
});
106-
107-
it("throws error when authorization endpoint is not configured", async () => {
108-
const providerWithoutAuth = new ProxyOAuthServerProvider({
109-
...baseOptions,
110-
endpoints: { ...baseOptions.endpoints, authorizationUrl: undefined },
111-
});
112-
113-
await expect(
114-
providerWithoutAuth.authorize(validClient, {
115-
redirectUri: "https://example.com/callback",
116-
codeChallenge: "test-challenge",
117-
}, mockResponse)
118-
).rejects.toThrow("No authorization endpoint configured");
119-
});
120106
});
121107

122108
describe("token exchange", () => {
@@ -128,7 +114,7 @@ describe("Proxy OAuth Server Provider", () => {
128114
};
129115

130116
beforeEach(() => {
131-
(global.fetch as jest.Mock).mockImplementation(() =>
117+
(global.fetch as jest.Mock).mockImplementation(() =>
132118
Promise.resolve({
133119
ok: true,
134120
json: () => Promise.resolve(mockTokenResponse),
@@ -176,23 +162,6 @@ describe("Proxy OAuth Server Provider", () => {
176162
expect(tokens).toEqual(mockTokenResponse);
177163
});
178164

179-
it("throws error when token endpoint is not configured", async () => {
180-
const providerWithoutToken = new ProxyOAuthServerProvider({
181-
...baseOptions,
182-
endpoints: { ...baseOptions.endpoints, tokenUrl: undefined },
183-
});
184-
185-
await expect(
186-
providerWithoutToken.exchangeAuthorizationCode(validClient, "test-code")
187-
).rejects.toThrow("No token endpoint configured");
188-
});
189-
190-
it("handles token exchange failure", async () => {
191-
mockFailedResponse();
192-
await expect(
193-
provider.exchangeAuthorizationCode(validClient, "invalid-code")
194-
).rejects.toThrow(ServerError);
195-
});
196165
});
197166

198167
describe("client registration", () => {
@@ -202,7 +171,7 @@ describe("Proxy OAuth Server Provider", () => {
202171
redirect_uris: ["https://new-client.com/callback"],
203172
};
204173

205-
(global.fetch as jest.Mock).mockImplementation(() =>
174+
(global.fetch as jest.Mock).mockImplementation(() =>
206175
Promise.resolve({
207176
ok: true,
208177
json: () => Promise.resolve(newClient),
@@ -239,7 +208,7 @@ describe("Proxy OAuth Server Provider", () => {
239208

240209
describe("token revocation", () => {
241210
it("revokes token", async () => {
242-
(global.fetch as jest.Mock).mockImplementation(() =>
211+
(global.fetch as jest.Mock).mockImplementation(() =>
243212
Promise.resolve({
244213
ok: true,
245214
})

src/server/auth/proxyProvider.ts

Lines changed: 28 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Response } from "express";
22
import { OAuthRegisteredClientsStore } from "./clients.js";
3-
import {
4-
OAuthClientInformationFull,
5-
OAuthClientInformationFullSchema,
6-
OAuthTokenRevocationRequest,
3+
import {
4+
OAuthClientInformationFull,
5+
OAuthClientInformationFullSchema,
6+
OAuthTokenRevocationRequest,
77
OAuthTokens,
88
OAuthTokensSchema,
99
} from "./../../shared/auth.js";
@@ -12,8 +12,8 @@ import { AuthorizationParams, OAuthServerProvider } from "./provider.js";
1212
import { ServerError } from "./errors.js";
1313

1414
export type ProxyEndpoints = {
15-
authorizationUrl?: string;
16-
tokenUrl?: string;
15+
authorizationUrl: string;
16+
tokenUrl: string;
1717
revocationUrl?: string;
1818
registrationUrl?: string;
1919
};
@@ -24,14 +24,14 @@ export type ProxyOptions = {
2424
*/
2525
endpoints: ProxyEndpoints;
2626

27-
/**
28-
* Function to verify access tokens and return auth info
29-
*/
30-
verifyAccessToken: (token: string) => Promise<AuthInfo>;
27+
/**
28+
* Function to verify access tokens and return auth info
29+
*/
30+
verifyAccessToken: (token: string) => Promise<AuthInfo>;
3131

32-
/**
33-
* Function to fetch client information from the upstream server
34-
*/
32+
/**
33+
* Function to fetch client information from the upstream server
34+
*/
3535
getClient: (clientId: string) => Promise<OAuthClientInformationFull | undefined>;
3636

3737
};
@@ -45,7 +45,7 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
4545
protected readonly _getClient: (clientId: string) => Promise<OAuthClientInformationFull | undefined>;
4646

4747
revokeToken?: (
48-
client: OAuthClientInformationFull,
48+
client: OAuthClientInformationFull,
4949
request: OAuthTokenRevocationRequest
5050
) => Promise<void>;
5151

@@ -55,15 +55,15 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
5555
this._getClient = options.getClient;
5656
if (options.endpoints?.revocationUrl) {
5757
this.revokeToken = async (
58-
client: OAuthClientInformationFull,
58+
client: OAuthClientInformationFull,
5959
request: OAuthTokenRevocationRequest
6060
) => {
6161
const revocationUrl = this._endpoints.revocationUrl;
62-
62+
6363
if (!revocationUrl) {
6464
throw new Error("No revocation endpoint configured");
6565
}
66-
66+
6767
const params = new URLSearchParams();
6868
params.set("token", request.token);
6969
params.set("client_id", client.client_id);
@@ -73,15 +73,15 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
7373
if (request.token_type_hint) {
7474
params.set("token_type_hint", request.token_type_hint);
7575
}
76-
76+
7777
const response = await fetch(revocationUrl, {
7878
method: "POST",
7979
headers: {
8080
"Content-Type": "application/x-www-form-urlencoded",
8181
},
8282
body: params.toString(),
8383
});
84-
84+
8585
if (!response.ok) {
8686
throw new ServerError(`Token revocation failed: ${response.status}`);
8787
}
@@ -115,18 +115,12 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
115115
}
116116

117117
async authorize(
118-
client: OAuthClientInformationFull,
119-
params: AuthorizationParams,
118+
client: OAuthClientInformationFull,
119+
params: AuthorizationParams,
120120
res: Response
121121
): Promise<void> {
122-
const authorizationUrl = this._endpoints.authorizationUrl;
123-
124-
if (!authorizationUrl) {
125-
throw new Error("No authorization endpoint configured");
126-
}
127-
128122
// Start with required OAuth parameters
129-
const targetUrl = new URL(authorizationUrl);
123+
const targetUrl = new URL(this._endpoints.authorizationUrl);
130124
const searchParams = new URLSearchParams({
131125
client_id: client.client_id,
132126
response_type: "code",
@@ -144,7 +138,7 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
144138
}
145139

146140
async challengeForAuthorizationCode(
147-
_client: OAuthClientInformationFull,
141+
_client: OAuthClientInformationFull,
148142
_authorizationCode: string
149143
): Promise<string> {
150144
// In a proxy setup, we don't store the code challenge ourselves
@@ -153,16 +147,10 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
153147
}
154148

155149
async exchangeAuthorizationCode(
156-
client: OAuthClientInformationFull,
150+
client: OAuthClientInformationFull,
157151
authorizationCode: string,
158152
codeVerifier?: string
159153
): Promise<OAuthTokens> {
160-
const tokenUrl = this._endpoints.tokenUrl;
161-
162-
if (!tokenUrl) {
163-
throw new Error("No token endpoint configured");
164-
}
165-
166154
const params = new URLSearchParams({
167155
grant_type: "authorization_code",
168156
client_id: client.client_id,
@@ -177,7 +165,7 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
177165
params.append("code_verifier", codeVerifier);
178166
}
179167

180-
const response = await fetch(tokenUrl, {
168+
const response = await fetch(this._endpoints.tokenUrl, {
181169
method: "POST",
182170
headers: {
183171
"Content-Type": "application/x-www-form-urlencoded",
@@ -195,15 +183,10 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
195183
}
196184

197185
async exchangeRefreshToken(
198-
client: OAuthClientInformationFull,
186+
client: OAuthClientInformationFull,
199187
refreshToken: string,
200188
scopes?: string[]
201189
): Promise<OAuthTokens> {
202-
const tokenUrl = this._endpoints.tokenUrl;
203-
204-
if (!tokenUrl) {
205-
throw new Error("No token endpoint configured");
206-
}
207190

208191
const params = new URLSearchParams({
209192
grant_type: "refresh_token",
@@ -219,7 +202,7 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
219202
params.set("scope", scopes.join(" "));
220203
}
221204

222-
const response = await fetch(tokenUrl, {
205+
const response = await fetch(this._endpoints.tokenUrl, {
223206
method: "POST",
224207
headers: {
225208
"Content-Type": "application/x-www-form-urlencoded",
@@ -237,5 +220,5 @@ export class ProxyOAuthServerProvider implements OAuthServerProvider {
237220

238221
async verifyAccessToken(token: string): Promise<AuthInfo> {
239222
return this._verifyAccessToken(token);
240-
}
223+
}
241224
}

0 commit comments

Comments
 (0)