Skip to content

Commit 45b68c9

Browse files
authored
Merge pull request #882 from brendandburns/tsc
Defer loading of OIDC until it's actually used.
2 parents a01b704 + 6ea7bb9 commit 45b68c9

File tree

3 files changed

+215
-2
lines changed

3 files changed

+215
-2
lines changed

src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
import { ExecAuth } from './exec_auth';
2727
import { FileAuth } from './file_auth';
2828
import { GoogleCloudPlatformAuth } from './gcp_auth';
29-
import { OpenIDConnectAuth } from './oidc_auth';
29+
import { DelayedOpenIDConnectAuth } from './oidc_auth_delayed';
3030

3131
// fs.existsSync was removed in node 10
3232
function fileExists(filepath: string): boolean {
@@ -44,7 +44,7 @@ export class KubeConfig {
4444
new GoogleCloudPlatformAuth(),
4545
new ExecAuth(),
4646
new FileAuth(),
47-
new OpenIDConnectAuth(),
47+
new DelayedOpenIDConnectAuth(),
4848
];
4949

5050
/**

src/oidc_auth_delayed.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import https = require('https');
2+
import request = require('request');
3+
4+
import { Authenticator } from './auth';
5+
import { User } from './config_types';
6+
7+
export class DelayedOpenIDConnectAuth implements Authenticator {
8+
private delegate?: Authenticator;
9+
10+
public constructor() {
11+
this.delegate = undefined;
12+
}
13+
14+
public isAuthProvider(user: User): boolean {
15+
if (!user.authProvider) {
16+
return false;
17+
}
18+
return user.authProvider.name === 'oidc';
19+
}
20+
21+
/**
22+
* Setup the authentication header for oidc authed clients
23+
* @param user user info
24+
* @param opts request options
25+
* @param overrideClient for testing, a preconfigured oidc client
26+
*/
27+
public async applyAuthentication(
28+
user: User,
29+
opts: request.Options | https.RequestOptions,
30+
overrideClient?: any,
31+
): Promise<void> {
32+
const oidc = await import('./oidc_auth');
33+
return new oidc.OpenIDConnectAuth().applyAuthentication(user, opts, overrideClient);
34+
}
35+
}

src/oidc_auth_delayed_test.ts

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import { expect } from 'chai';
2+
import * as request from 'request';
3+
import { base64url } from 'rfc4648';
4+
import { TextEncoder } from 'util';
5+
6+
import { User } from './config_types';
7+
import { DelayedOpenIDConnectAuth } from './oidc_auth_delayed';
8+
9+
function encode(value: string): string {
10+
return base64url.stringify(new TextEncoder().encode(value));
11+
}
12+
13+
function makeJWT(header: string, payload: object, signature: string): string {
14+
return encode(header) + '.' + encode(JSON.stringify(payload)) + '.' + encode(signature);
15+
}
16+
17+
describe('OIDCAuth', () => {
18+
var auth: DelayedOpenIDConnectAuth;
19+
beforeEach(() => {
20+
auth = new DelayedOpenIDConnectAuth();
21+
});
22+
23+
it('should be true for oidc user', () => {
24+
const user = {
25+
authProvider: {
26+
name: 'oidc',
27+
},
28+
} as User;
29+
30+
expect(auth.isAuthProvider(user)).to.equal(true);
31+
});
32+
33+
it('should be false for other user', () => {
34+
const user = {
35+
authProvider: {
36+
name: 'azure',
37+
},
38+
} as User;
39+
40+
expect(auth.isAuthProvider(user)).to.equal(false);
41+
});
42+
43+
it('should be false for null user.authProvider', () => {
44+
const user = {} as User;
45+
46+
expect(auth.isAuthProvider(user)).to.equal(false);
47+
});
48+
49+
it('authorization should be undefined if token missing', async () => {
50+
const user = {
51+
authProvider: {
52+
name: 'oidc',
53+
config: {
54+
'client-id': 'id',
55+
'client-secret': 'clientsecret',
56+
'refresh-token': 'refreshtoken',
57+
'idp-issuer-url': 'https://www.google.com/',
58+
},
59+
},
60+
} as User;
61+
62+
const opts = {} as request.Options;
63+
opts.headers = [];
64+
await auth.applyAuthentication(user, opts);
65+
expect(opts.headers.Authorization).to.be.undefined;
66+
});
67+
68+
it('authorization should be undefined if client-id missing', async () => {
69+
const past = 100;
70+
const token = makeJWT('{}', { exp: past }, 'fake');
71+
const user = {
72+
authProvider: {
73+
name: 'oidc',
74+
config: {
75+
'id-token': token,
76+
'client-secret': 'clientsecret',
77+
'refresh-token': 'refreshtoken',
78+
'idp-issuer-url': 'https://www.google.com/',
79+
},
80+
},
81+
} as User;
82+
83+
const opts = {} as request.Options;
84+
opts.headers = [];
85+
await auth.applyAuthentication(user, opts);
86+
expect(opts.headers.Authorization).to.be.undefined;
87+
});
88+
89+
it('authorization should be work if client-secret missing', async () => {
90+
const user = {
91+
authProvider: {
92+
name: 'oidc',
93+
config: {
94+
'id-token': 'fakeToken',
95+
'client-id': 'id',
96+
'refresh-token': 'refreshtoken',
97+
'idp-issuer-url': 'https://www.google.com/',
98+
},
99+
},
100+
} as User;
101+
102+
const opts = {} as request.Options;
103+
opts.headers = [];
104+
(auth as any).currentTokenExpiration = Date.now() / 1000 + 1000;
105+
await auth.applyAuthentication(user, opts, {
106+
refresh: () => {
107+
return {
108+
id_token: 'fakeToken',
109+
refresh_token: 'fakerToken',
110+
};
111+
},
112+
});
113+
expect(opts.headers.Authorization).to.equal('Bearer fakeToken');
114+
});
115+
116+
it('authorization should be undefined if refresh-token missing', async () => {
117+
const past = 100;
118+
const token = makeJWT('{}', { exp: past }, 'fake');
119+
const user = {
120+
authProvider: {
121+
name: 'oidc',
122+
config: {
123+
'id-token': token,
124+
'client-id': 'id',
125+
'client-secret': 'clientsecret',
126+
'idp-issuer-url': 'https://www.google.com/',
127+
},
128+
},
129+
} as User;
130+
131+
const opts = {} as request.Options;
132+
opts.headers = [];
133+
await auth.applyAuthentication(user, opts);
134+
expect(opts.headers.Authorization).to.be.undefined;
135+
});
136+
137+
it('authorization should work if refresh-token missing but token is unexpired', async () => {
138+
const future = Date.now() / 1000 + 1000000;
139+
const token = makeJWT('{}', { exp: future }, 'fake');
140+
const user = {
141+
authProvider: {
142+
name: 'oidc',
143+
config: {
144+
'id-token': token,
145+
'client-id': 'id',
146+
'client-secret': 'clientsecret',
147+
'idp-issuer-url': 'https://www.google.com/',
148+
},
149+
},
150+
} as User;
151+
152+
const opts = {} as request.Options;
153+
opts.headers = [];
154+
await auth.applyAuthentication(user, opts);
155+
expect(opts.headers.Authorization).to.equal(`Bearer ${token}`);
156+
});
157+
158+
it('authorization should be undefined if idp-issuer-url missing', async () => {
159+
const past = 100;
160+
const token = makeJWT('{}', { exp: past }, 'fake');
161+
const user = {
162+
authProvider: {
163+
name: 'oidc',
164+
config: {
165+
'id-token': token,
166+
'client-id': 'id',
167+
'client-secret': 'clientsecret',
168+
'refresh-token': 'refreshtoken',
169+
},
170+
},
171+
} as User;
172+
173+
const opts = {} as request.Options;
174+
opts.headers = [];
175+
await auth.applyAuthentication(user, opts, {});
176+
expect(opts.headers.Authorization).to.be.undefined;
177+
});
178+
});

0 commit comments

Comments
 (0)