Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Commit

Permalink
fix: use grantManager to parse token in onSubscriptionConnect
Browse files Browse the repository at this point in the history
  • Loading branch information
darahayes committed Jan 6, 2020
1 parent 0d457eb commit f11a2dc
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 83 deletions.
20 changes: 11 additions & 9 deletions src/KeycloakSubscriptionHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Keycloak from './KeycloakTypings'
import { Token } from './KeycloakToken'
import { KeycloakSubscriptionHandlerOptions } from './api'

/**
Expand Down Expand Up @@ -55,7 +54,7 @@ export class KeycloakSubscriptionHandler {
* @param webSocket
* @param context
*/
public async onSubscriptionConnect(connectionParams: any, webSocket: any, context: any): Promise<Keycloak.Token | undefined | Error> {
public async onSubscriptionConnect(connectionParams: any, webSocket: any, context: any): Promise<Keycloak.Token | undefined> {
if (!connectionParams || typeof connectionParams !== 'object') {
if (this.protect === true) {
throw new Error('Access Denied - missing connection parameters for Authentication')
Expand All @@ -72,20 +71,23 @@ export class KeycloakSubscriptionHandler {
}
return
}
const token = this.getBearerTokenFromHeader(header, this.keycloak.config.clientId)
try {
await this.keycloak.grantManager.validateToken(token, 'Bearer')
//@ts-ignore
return token
const accessToken = this.getAccessTokenFromHeader(header)
const grant = await this.keycloak.grantManager.createGrant(accessToken)
return grant.access_token as unknown as Keycloak.Token
} catch (e) {
throw new Error(`Access Denied - ${e}`)
}
}

private getBearerTokenFromHeader(header: any, clientId?: string) {
private getAccessTokenFromHeader(header: any) {
if (header && typeof header === 'string' && (header.indexOf('bearer ') === 0 || header.indexOf('Bearer ') === 0)) {
const token = header.substring(7)
return new Token(token, clientId)
const tokenString = header.substring(7)
return {
access_token: tokenString
};
} else {
throw new Error('Invalid Authorization field in connection params. Must be in the format "Authorization": "Bearer <token string>"')
}
}
}
126 changes: 52 additions & 74 deletions test/KeycloakSubscriptionHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import test from 'ava'
import Keycloak from '../src/KeycloakTypings'

import { KeycloakSubscriptionHandler } from '../src/KeycloakSubscriptionHandler'
import { Token } from '../src/KeycloakToken';
import { Token } from './utils/KeycloakToken';

const TEST_CLIENT_ID = 'voyager-testing'

test('onSubscriptionConnect throws if no keycloak provided', async t => {
t.throws(() => {
Expand All @@ -13,14 +15,9 @@ test('onSubscriptionConnect throws if no keycloak provided', async t => {

test('onSubscriptionConnect throws if no connectionParams Provided', async t => {
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
return new Promise((resolve, reject) => {
resolve(true)
})
createGrant: (token: any) => {
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak
Expand All @@ -34,14 +31,9 @@ test('onSubscriptionConnect throws if no connectionParams Provided', async t =>

test('onSubscriptionConnect throws if no connectionParams is not an object', async t => {
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
return new Promise((resolve, reject) => {
resolve(true)
})
createGrant: (token: any) => {
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak
Expand All @@ -56,14 +48,9 @@ test('onSubscriptionConnect throws if no connectionParams is not an object', asy

test('onSubscriptionConnect throws if no Auth provided', async t => {
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
return new Promise((resolve, reject) => {
resolve(true)
})
createGrant: (token: any) => {
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak
Expand All @@ -76,16 +63,28 @@ test('onSubscriptionConnect throws if no Auth provided', async t => {
}, 'Access Denied - missing Authorization field in connection parameters')
})

test('onSubscriptionConnect throws if "Authorization" field is not formed correctly', async t => {
const stubKeycloak = {
grantManager: {
createGrant: (token: any) => {
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak

const subscriptionHandler = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const connectionParams = { Authorization: '1234' }

await t.throwsAsync(async () => {
await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {})
}, 'Access Denied - Error: Invalid Authorization field in connection params. Must be in the format "Authorization": "Bearer <token string>"')
})

test('onSubscriptionConnect returns a token Object if the keycloak library considers it valid', async t => {
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
return new Promise((resolve, reject) => {
resolve(true)
})
createGrant: (token: any) => {
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak
Expand All @@ -96,19 +95,16 @@ test('onSubscriptionConnect returns a token Object if the keycloak library consi
const connectionParams = { Authorization: tokenString }

const token = await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {})
t.truthy(token instanceof Token)
t.truthy(token)
//@ts-ignore
t.truthy(token.content)
})

test('onSubscriptionConnect can also parse the token with lowercase \'bearer\'', async t => {
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
return new Promise((resolve, reject) => {
resolve(true)
})
createGrant: (token: any) => {
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak
Expand All @@ -119,19 +115,18 @@ test('onSubscriptionConnect can also parse the token with lowercase \'bearer\'',
const connectionParams = { Authorization: tokenString }

const token = await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {})
t.truthy(token instanceof Token)

t.truthy(token)
//@ts-ignore
t.truthy(token.content)
})

test('the token object will have hasRole, hasRealmRole and hasPermissions if the', async t => {
test('the token object will have hasRole function if grant is successfully created', async t => {
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
return new Promise((resolve, reject) => {
resolve(true)
})
createGrant: (token: any) => {
console.log(`createGant called with token String ${tokenString}`)
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak
Expand All @@ -143,19 +138,17 @@ test('the token object will have hasRole, hasRealmRole and hasPermissions if the
const subscriptionHandler = new KeycloakSubscriptionHandler({ keycloak: stubKeycloak })
const connectionParams = { Authorization: tokenString, clientId: 'voyager-testing' }

const token = await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {}) as Token
t.truthy(token instanceof Token)
const token = await subscriptionHandler.onSubscriptionConnect(connectionParams, {}, {})
t.truthy(token)
//@ts-ignore
t.truthy(token.hasRole('tester'))
})

test('If the keycloak token validation fails, then onSubscriptionConnect will throw', async t => {
test('If grant creation fails then onSubscriptionConnect will throw', async t => {
const errorMsg = 'token is invalid'
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
createGrant: (token: any) => {
return new Promise((resolve, reject) => {
reject(new Error(errorMsg))
})
Expand All @@ -177,14 +170,9 @@ test('If the keycloak token validation fails, then onSubscriptionConnect will th

test('onSubscriptionConnect with {protect: false} does not throw if no connectionParams Provided', async t => {
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
return new Promise((resolve, reject) => {
resolve(true)
})
createGrant: (token: any) => {
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak
Expand All @@ -198,14 +186,9 @@ test('onSubscriptionConnect with {protect: false} does not throw if no connectio

test('onSubscriptionConnect with {protect: false} does not throw if connectionParams is not an object', async t => {
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
return new Promise((resolve, reject) => {
resolve(true)
})
createGrant: (token: any) => {
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak
Expand All @@ -220,14 +203,9 @@ test('onSubscriptionConnect with {protect: false} does not throw if connectionPa

test('onSubscriptionConnect with {protect: false} does not throw if no Auth provided', async t => {
const stubKeycloak = {
config: {
clientId: 'voyager-testing',
},
grantManager: {
validateToken: (token: string, type: 'string') => {
return new Promise((resolve, reject) => {
resolve(true)
})
createGrant: (token: any) => {
return { access_token: new Token(token.access_token, TEST_CLIENT_ID)}
}
}
} as unknown as Keycloak.Keycloak
Expand Down
File renamed without changes.

0 comments on commit f11a2dc

Please sign in to comment.