UNPKG

keycloak-connect

Version:
355 lines (306 loc) 10.3 kB
/* * Copyright 2017 Red Hat Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ 'use strict' const admin = require('./utils/realm') const NodeApp = require('./fixtures/node-console/index').NodeApp const TestVector = require('./utils/helper').TestVector const test = require('blue-tape') const axios = require('axios') const getToken = require('./utils/token') const realmName = 'mixed-mode-realm' const realmManager = admin.createRealm(realmName) const app = new NodeApp() const auth = { username: 'test-admin', password: 'password' } const getSessionCookie = response => response.headers['set-cookie'][0].split(';')[0] const assertAlternativeMessages = (assert, message, ...messages) => { assert.true(messages.includes(message), `Message "${message}" is not included in provided messages`) } test('setup', t => { return realmManager.then(() => { return admin.createClient(app.confidential(), realmName) .then((installation) => { return app.build(installation) }) }) }) test('Should test protected route.', t => { t.plan(1) const opt = { url: `${app.address}/service/admin` } return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for no credentials') }) test('Should test protected route with admin credentials.', t => { t.plan(1) return getToken({ realmName }).then((token) => { const opt = { url: `${app.address}/service/admin`, headers: { Authorization: `Bearer ${token}` } } return axios(opt) .then(response => { t.equal(response.data.message, 'admin') }) .catch(error => { t.fail(error.response.data) }) }) }) test('Should test protected route with invalid access token.', t => { t.plan(1) return getToken({ realmName }).then((token) => { const opt = { url: `${app.address}/service/admin`, headers: { Authorization: 'Bearer ' + token.replace(/(.+?\..+?\.).*/, '$1.Invalid') } } return t.shouldFail(axios(opt), 'Access denied', 'Response should be access denied for invalid access token') }) }) test('Should handle direct access grants.', t => { t.plan(3) return axios.post(`${app.address}/service/grant`, auth) .then(response => { t.ok(response.data.id_token, 'Response should contain an id_token') t.ok(response.data.access_token, 'Response should contain an access_token') t.ok(response.data.refresh_token, 'Response should contain an refresh_token') }) .catch(error => { t.fail(error.response.data) }) }) test('Should store the grant.', t => { t.plan(3) const endpoint = `${app.address}/service/grant` return axios.post(endpoint, auth) .then(response => getSessionCookie(response)) .then(cookie => { return axios.get(endpoint, { headers: { cookie } }) .then(response => { t.ok(response.data.id_token, 'Response should contain an id_token') t.ok(response.data.access_token, 'Response should contain an access_token') t.ok(response.data.refresh_token, 'Response should contain an refresh_token') }) }) }) test('Should not store grant on bearer request', t => { t.plan(4) const endpoint = `${app.address}/service/grant` let sessionCookie return axios.post(endpoint, auth) .then(response => { const data = { cookie: getSessionCookie(response), grant: response.data } sessionCookie = data.cookie return data }) .then(data => { const opt = { url: `${app.address}/service/secured`, headers: { Authorization: 'Bearer ' + data.grant.access_token.token, Cookie: data.cookie } } return axios(opt) .then(response => { t.equal(response.data.message, 'secured') const opt = { url: endpoint, headers: { Cookie: sessionCookie } } return axios(opt) .then(response => { t.ok(response.data.id_token, 'Response should contain an id_token') t.ok(response.data.access_token, 'Response should contain an access_token') t.ok(response.data.refresh_token, 'Response should contain an refresh_token') }) }) }) }) test('Should test admin logout endpoint with incomplete payload', t => { t.plan(2) const app = new NodeApp() const client = admin.createClient(app.confidential('adminapp'), realmName) return client.then((installation) => { app.build(installation) const opt = { method: 'post', url: `${app.address}/k_logout`, data: TestVector.logoutIncompletePayload } return axios(opt).catch(err => { t.equal(err.response.status, 401) assertAlternativeMessages(t, err.response.data, "Cannot read property 'kid' of undefined", "Cannot read properties of undefined (reading 'kid')" ) app.destroy() }) }).then(() => { app.destroy() }) }) test('Should test admin logout endpoint with payload signed by a different key pair', t => { t.plan(2) const app = new NodeApp() const client = admin.createClient(app.confidential('adminapp2'), realmName) return client.then((installation) => { app.build(installation) const opt = { method: 'post', url: `${app.address}/k_logout`, data: TestVector.logoutWrongKeyPairPayload } return axios(opt).catch(err => { t.equal(err.response.status, 401) t.equal(err.response.data, 'admin request failed: invalid token (signature)') app.destroy() }) }).then(() => { app.destroy() }) }) test('Should test admin logout endpoint with valid payload', t => { t.plan(1) const app = new NodeApp() const client = admin.createClient(app.confidential('adminapp3'), realmName) return client.then((installation) => { app.build(installation) const opt = { method: 'post', url: `${app.address}/k_logout`, data: TestVector.logoutValidPayload } return axios(opt).then(response => { t.equal(response.status, 200) }).catch(err => { t.fail(err.response.data) }) }).then(() => { app.destroy() }) }) test('Should test admin push_not_before endpoint with incomplete payload', t => { t.plan(2) const app = new NodeApp() const client = admin.createClient(app.confidential('adminapp5'), realmName) return client.then((installation) => { app.build(installation) const opt = { method: 'post', url: `${app.address}/k_push_not_before`, data: TestVector.notBeforeIncompletePayload } return axios(opt).catch(err => { t.equal(err.response.status, 401) assertAlternativeMessages(t, err.response.data, "Cannot read property 'kid' of undefined", "Cannot read properties of undefined (reading 'kid')" ) app.destroy() }) }).then(() => { app.destroy() }) }) test('Should test admin push_not_before endpoint with payload signed by a different key pair', t => { t.plan(2) const app = new NodeApp() const client = admin.createClient(app.confidential('adminapp6'), realmName) return client.then((installation) => { app.build(installation) const opt = { method: 'post', url: `${app.address}/k_push_not_before`, data: TestVector.notBeforeWrongKeyPairPayload } return axios(opt).catch(err => { t.equal(err.response.status, 401) t.equal(err.response.data, 'admin request failed: invalid token (signature)') app.destroy() }) }).then(() => { app.destroy() }) }) test('Should verify during authentication if the token contains the client name as audience.', t => { t.plan(3) const someapp = new NodeApp() const client = admin.createClient(someapp.confidential('audience-app'), realmName) return client.then((installation) => { installation.verifyTokenAudience = true someapp.build(installation) return axios.post(`${someapp.address}/service/grant`, auth) .then(response => { t.ok(response.data.id_token, 'Response should contain an id_token') t.ok(response.data.access_token, 'Response should contain an access_token') t.ok(response.data.refresh_token, 'Response should contain an refresh_token') }) .catch(error => { t.fail(error.response.data) }) }).then(() => { someapp.destroy() }) }) test('Should test admin push_not_before endpoint with valid payload', t => { t.plan(1) const app = new NodeApp() const client = admin.createClient(app.confidential('adminapp7'), realmName) return client.then((installation) => { app.build(installation) const opt = { method: 'post', url: `${app.address}/k_push_not_before`, data: TestVector.notBeforeValidPayload } return axios(opt).then(response => { t.equal(response.status, 200) }).catch(err => { t.fail(err.response.data) }) }).then(() => { app.destroy() }) }) test.skip('Should logout with redirect url', t => { t.plan(1) const serviceEndpoint = `${app.address}/service/grant` const logoutEndpoint = `${app.address}/logout?redirect_url=http%3A%2F%2Flocalhost%3A${app.port}%2Fbye` return axios.post(serviceEndpoint, auth) .then(response => getSessionCookie(response)) .then(cookie => { return axios.get(logoutEndpoint, { headers: { cookie } }) .then(response => { t.assert(response.request.path, '/bye', 'Expected redirect after logout') }) }) }) test('teardown', t => { return realmManager.then((realm) => { app.destroy() admin.destroy(realmName) }) })