Skip to content

Commit 419f286

Browse files
committed
fix: check for native logout redirect allowed same way as during auth
resolves: #1351
1 parent 362a724 commit 419f286

File tree

2 files changed

+77
-8
lines changed

2 files changed

+77
-8
lines changed

lib/models/client.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,11 @@ export default function getClient(provider) {
428428
return this.grantTypes.includes(type);
429429
}
430430

431-
redirectUriAllowed(value) {
431+
#redirectAllowed(value, allowedUris) {
432432
const parsed = URL.parse(value);
433433
if (!parsed) return false;
434434

435-
const match = this.redirectUris.find((allowed) => URL.parse(allowed)?.href === parsed.href);
435+
const match = allowedUris.find((allowed) => URL.parse(allowed)?.href === parsed.href);
436436
if (
437437
!!match
438438
|| this.applicationType !== 'native'
@@ -444,7 +444,7 @@ export default function getClient(provider) {
444444

445445
parsed.port = '';
446446

447-
return !!this.redirectUris
447+
return !!allowedUris
448448
.find((allowed) => {
449449
const registered = URL.parse(allowed);
450450
if (!registered) return false;
@@ -453,11 +453,12 @@ export default function getClient(provider) {
453453
});
454454
}
455455

456+
redirectUriAllowed(value) {
457+
return this.#redirectAllowed(value, this.redirectUris);
458+
}
459+
456460
postLogoutRedirectUriAllowed(value) {
457-
const parsed = URL.parse(value);
458-
if (!parsed) return false;
459-
return !!this.postLogoutRedirectUris
460-
.find((allowed) => URL.parse(allowed)?.href === parsed.href);
461+
return this.#redirectAllowed(value, this.postLogoutRedirectUris);
461462
}
462463

463464
static async validate(metadata) {

test/oauth_native_apps/oauth_native_apps.test.js

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,14 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
7474
response_types: ['id_token'],
7575
token_endpoint_auth_method: 'none',
7676
redirect_uris: ['http://127.0.0.1:2355/op/callback'],
77+
post_logout_redirect_uris: ['http://127.0.0.1:2355/op/logout'],
7778
}).then((client) => {
7879
expect(client.redirectUriAllowed('http:')).to.be.false;
7980
expect(client.redirectUriAllowed('http://127.0.0.')).to.be.false;
8081
expect(client.redirectUriAllowed('http://127.0.0.1::')).to.be.false;
82+
expect(client.postLogoutRedirectUriAllowed('http:')).to.be.false;
83+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.')).to.be.false;
84+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1::')).to.be.false;
8185
});
8286
});
8387

@@ -89,13 +93,21 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
8993
response_types: ['id_token'],
9094
token_endpoint_auth_method: 'none',
9195
redirect_uris: ['http://localhost:2355/op/callback'],
96+
post_logout_redirect_uris: ['http://localhost:2355/op/logout'],
9297
}).then((client) => {
9398
expect(client.redirectUris).to.contain('http://localhost:2355/op/callback');
9499
expect(client.redirectUriAllowed('http://localhost/op/callback')).to.be.true;
95100
expect(client.redirectUriAllowed('http://localhost:80/op/callback')).to.be.true;
96101
expect(client.redirectUriAllowed('http://localhost:443/op/callback')).to.be.true;
97102
expect(client.redirectUriAllowed('http://localhost:2355/op/callback')).to.be.true;
98103
expect(client.redirectUriAllowed('http://localhost:8888/op/callback')).to.be.true;
104+
105+
expect(client.postLogoutRedirectUris).to.contain('http://localhost:2355/op/logout');
106+
expect(client.postLogoutRedirectUriAllowed('http://localhost/op/logout')).to.be.true;
107+
expect(client.postLogoutRedirectUriAllowed('http://localhost:80/op/logout')).to.be.true;
108+
expect(client.postLogoutRedirectUriAllowed('http://localhost:443/op/logout')).to.be.true;
109+
expect(client.postLogoutRedirectUriAllowed('http://localhost:2355/op/logout')).to.be.true;
110+
expect(client.postLogoutRedirectUriAllowed('http://localhost:8888/op/logout')).to.be.true;
99111
});
100112
});
101113

@@ -107,13 +119,21 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
107119
response_types: ['id_token'],
108120
token_endpoint_auth_method: 'none',
109121
redirect_uris: ['http://localhost/op/callback'],
122+
post_logout_redirect_uris: ['http://localhost/op/logout'],
110123
}).then((client) => {
111124
expect(client.redirectUris).to.contain('http://localhost/op/callback');
112125
expect(client.redirectUriAllowed('http://localhost/op/callback')).to.be.true;
113126
expect(client.redirectUriAllowed('http://localhost:80/op/callback')).to.be.true;
114127
expect(client.redirectUriAllowed('http://localhost:443/op/callback')).to.be.true;
115128
expect(client.redirectUriAllowed('http://localhost:2355/op/callback')).to.be.true;
116129
expect(client.redirectUriAllowed('http://localhost:8888/op/callback')).to.be.true;
130+
131+
expect(client.postLogoutRedirectUris).to.contain('http://localhost/op/logout');
132+
expect(client.postLogoutRedirectUriAllowed('http://localhost/op/logout')).to.be.true;
133+
expect(client.postLogoutRedirectUriAllowed('http://localhost:80/op/logout')).to.be.true;
134+
expect(client.postLogoutRedirectUriAllowed('http://localhost:443/op/logout')).to.be.true;
135+
expect(client.postLogoutRedirectUriAllowed('http://localhost:2355/op/logout')).to.be.true;
136+
expect(client.postLogoutRedirectUriAllowed('http://localhost:8888/op/logout')).to.be.true;
117137
});
118138
});
119139

@@ -125,13 +145,21 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
125145
response_types: ['id_token'],
126146
token_endpoint_auth_method: 'none',
127147
redirect_uris: ['http://127.0.0.1:2355/op/callback'],
148+
post_logout_redirect_uris: ['http://127.0.0.1:2355/op/logout'],
128149
}).then((client) => {
129150
expect(client.redirectUris).to.contain('http://127.0.0.1:2355/op/callback');
130151
expect(client.redirectUriAllowed('http://127.0.0.1/op/callback')).to.be.true;
131152
expect(client.redirectUriAllowed('http://127.0.0.1:80/op/callback')).to.be.true;
132153
expect(client.redirectUriAllowed('http://127.0.0.1:443/op/callback')).to.be.true;
133154
expect(client.redirectUriAllowed('http://127.0.0.1:2355/op/callback')).to.be.true;
134155
expect(client.redirectUriAllowed('http://127.0.0.1:8888/op/callback')).to.be.true;
156+
157+
expect(client.postLogoutRedirectUris).to.contain('http://127.0.0.1:2355/op/logout');
158+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1/op/logout')).to.be.true;
159+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1:80/op/logout')).to.be.true;
160+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1:443/op/logout')).to.be.true;
161+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1:2355/op/logout')).to.be.true;
162+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1:8888/op/logout')).to.be.true;
135163
});
136164
});
137165

@@ -143,13 +171,21 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
143171
response_types: ['id_token'],
144172
token_endpoint_auth_method: 'none',
145173
redirect_uris: ['http://127.0.0.1/op/callback'],
174+
post_logout_redirect_uris: ['http://127.0.0.1/op/logout'],
146175
}).then((client) => {
147176
expect(client.redirectUris).to.contain('http://127.0.0.1/op/callback');
148177
expect(client.redirectUriAllowed('http://127.0.0.1/op/callback')).to.be.true;
149178
expect(client.redirectUriAllowed('http://127.0.0.1:80/op/callback')).to.be.true;
150179
expect(client.redirectUriAllowed('http://127.0.0.1:443/op/callback')).to.be.true;
151180
expect(client.redirectUriAllowed('http://127.0.0.1:2355/op/callback')).to.be.true;
152181
expect(client.redirectUriAllowed('http://127.0.0.1:8888/op/callback')).to.be.true;
182+
183+
expect(client.postLogoutRedirectUris).to.contain('http://127.0.0.1/op/logout');
184+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1/op/logout')).to.be.true;
185+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1:80/op/logout')).to.be.true;
186+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1:443/op/logout')).to.be.true;
187+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1:2355/op/logout')).to.be.true;
188+
expect(client.postLogoutRedirectUriAllowed('http://127.0.0.1:8888/op/logout')).to.be.true;
153189
});
154190
});
155191

@@ -161,13 +197,21 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
161197
response_types: ['id_token'],
162198
token_endpoint_auth_method: 'none',
163199
redirect_uris: ['http://[::1]:2355/op/callback'],
200+
post_logout_redirect_uris: ['http://[::1]:2355/op/logout'],
164201
}).then((client) => {
165202
expect(client.redirectUris).to.contain('http://[::1]:2355/op/callback');
166203
expect(client.redirectUriAllowed('http://[::1]/op/callback')).to.be.true;
167204
expect(client.redirectUriAllowed('http://[::1]:80/op/callback')).to.be.true;
168205
expect(client.redirectUriAllowed('http://[::1]:443/op/callback')).to.be.true;
169206
expect(client.redirectUriAllowed('http://[::1]:2355/op/callback')).to.be.true;
170207
expect(client.redirectUriAllowed('http://[::1]:8888/op/callback')).to.be.true;
208+
209+
expect(client.postLogoutRedirectUris).to.contain('http://[::1]:2355/op/logout');
210+
expect(client.postLogoutRedirectUriAllowed('http://[::1]/op/logout')).to.be.true;
211+
expect(client.postLogoutRedirectUriAllowed('http://[::1]:80/op/logout')).to.be.true;
212+
expect(client.postLogoutRedirectUriAllowed('http://[::1]:443/op/logout')).to.be.true;
213+
expect(client.postLogoutRedirectUriAllowed('http://[::1]:2355/op/logout')).to.be.true;
214+
expect(client.postLogoutRedirectUriAllowed('http://[::1]:8888/op/logout')).to.be.true;
171215
});
172216
});
173217

@@ -179,17 +223,25 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
179223
response_types: ['id_token'],
180224
token_endpoint_auth_method: 'none',
181225
redirect_uris: ['http://[::1]/op/callback'],
226+
post_logout_redirect_uris: ['http://[::1]/op/logout'],
182227
}).then((client) => {
183228
expect(client.redirectUris).to.contain('http://[::1]/op/callback');
184229
expect(client.redirectUriAllowed('http://[::1]/op/callback')).to.be.true;
185230
expect(client.redirectUriAllowed('http://[::1]:80/op/callback')).to.be.true;
186231
expect(client.redirectUriAllowed('http://[::1]:443/op/callback')).to.be.true;
187232
expect(client.redirectUriAllowed('http://[::1]:2355/op/callback')).to.be.true;
188233
expect(client.redirectUriAllowed('http://[::1]:8888/op/callback')).to.be.true;
234+
235+
expect(client.postLogoutRedirectUris).to.contain('http://[::1]/op/logout');
236+
expect(client.postLogoutRedirectUriAllowed('http://[::1]/op/logout')).to.be.true;
237+
expect(client.postLogoutRedirectUriAllowed('http://[::1]:80/op/logout')).to.be.true;
238+
expect(client.postLogoutRedirectUriAllowed('http://[::1]:443/op/logout')).to.be.true;
239+
expect(client.postLogoutRedirectUriAllowed('http://[::1]:2355/op/logout')).to.be.true;
240+
expect(client.postLogoutRedirectUriAllowed('http://[::1]:8888/op/logout')).to.be.true;
189241
});
190242
});
191243

192-
it('rejects http protocol uris not using loopback uris', function () {
244+
it('rejects http protocol redirect_uris not using loopback uris', function () {
193245
return assert.rejects(addClient(this.provider, {
194246
application_type: 'native',
195247
client_id: 'native-custom',
@@ -203,6 +255,22 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
203255
return true;
204256
});
205257
});
258+
259+
it('rejects http protocol post_logout_redirect_uris not using loopback uris', function () {
260+
return assert.rejects(addClient(this.provider, {
261+
application_type: 'native',
262+
client_id: 'native-custom',
263+
grant_types: ['implicit'],
264+
response_types: ['id_token'],
265+
token_endpoint_auth_method: 'none',
266+
redirect_uris: ['http://[::1]/op/callback'],
267+
post_logout_redirect_uris: ['http://rp.example.com/op/logout'],
268+
}), (err) => {
269+
expect(err).to.have.property('message', 'invalid_client_metadata');
270+
expect(err).to.have.property('error_description', 'post_logout_redirect_uris for native clients using http as a protocol can only use loopback addresses as hostnames');
271+
return true;
272+
});
273+
});
206274
});
207275
});
208276
});

0 commit comments

Comments
 (0)