feat: add support for jwks-rsa (#1)
This commit is contained in:
@ -3,12 +3,12 @@ import { io } from 'socket.io-client'
|
||||
|
||||
import { fixtureStart, fixtureStop } from './fixture'
|
||||
|
||||
describe('authorize', () => {
|
||||
describe('authorize - with secret as string in options', () => {
|
||||
let token: string = ''
|
||||
|
||||
beforeEach((done) => {
|
||||
beforeEach(async (done) => {
|
||||
jest.setTimeout(15_000)
|
||||
fixtureStart(async () => {
|
||||
await fixtureStart(async () => {
|
||||
const response = await axios.post('http://localhost:9000/login')
|
||||
token = response.data.token
|
||||
done()
|
||||
@ -67,3 +67,37 @@ describe('authorize', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const secretCallback = async (): Promise<string> => {
|
||||
return 'somesecret'
|
||||
}
|
||||
|
||||
describe('authorize - with secret as callback in options', () => {
|
||||
let token: string = ''
|
||||
|
||||
beforeEach(async (done) => {
|
||||
jest.setTimeout(15_000)
|
||||
await fixtureStart(
|
||||
async () => {
|
||||
const response = await axios.post('http://localhost:9000/login')
|
||||
token = response.data.token
|
||||
done()
|
||||
},
|
||||
{ secret: secretCallback }
|
||||
)
|
||||
})
|
||||
|
||||
afterEach((done) => {
|
||||
fixtureStop(done)
|
||||
})
|
||||
|
||||
it('should connect the user', (done) => {
|
||||
const socket = io('http://localhost:9000', {
|
||||
extraHeaders: { Authorization: `Bearer ${token}` }
|
||||
})
|
||||
socket.on('connect', () => {
|
||||
socket.close()
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ import { Server as HttpsServer } from 'https'
|
||||
import { Server as SocketIoServer } from 'socket.io'
|
||||
import enableDestroy from 'server-destroy'
|
||||
|
||||
import { authorize } from '../../index'
|
||||
import { authorize, AuthorizeOptions } from '../../index'
|
||||
|
||||
interface Socket {
|
||||
io: null | SocketIoServer
|
||||
@ -21,16 +21,26 @@ const socket: Socket = {
|
||||
|
||||
let server: HttpServer | null = null
|
||||
|
||||
export const fixtureStart = (done: any): void => {
|
||||
const options = { secret: 'aaafoo super sercret' }
|
||||
export const fixtureStart = async (
|
||||
done: any,
|
||||
options: AuthorizeOptions = { secret: 'aaafoo super sercret' }
|
||||
): Promise<void> => {
|
||||
const app = express()
|
||||
app.use(express.json())
|
||||
let keySecret = 'secret'
|
||||
if (typeof options.secret === 'string') {
|
||||
keySecret = options.secret
|
||||
} else {
|
||||
keySecret = await options.secret(() => {})
|
||||
}
|
||||
app.post('/login', (_req, res) => {
|
||||
const profile = {
|
||||
email: 'john@doe.com',
|
||||
id: 123
|
||||
}
|
||||
const token = jwt.sign(profile, options.secret, { expiresIn: 60 * 60 * 5 })
|
||||
const token = jwt.sign(profile, keySecret, {
|
||||
expiresIn: 60 * 60 * 5
|
||||
})
|
||||
return res.json({ token })
|
||||
})
|
||||
server = app.listen(9000, done)
|
||||
|
@ -21,15 +21,17 @@ type SocketIOMiddleware = (
|
||||
next: (err?: ExtendedError) => void
|
||||
) => void
|
||||
|
||||
interface AuthorizeOptions {
|
||||
secret: string
|
||||
type SecretCallback = (decodedToken: null | { [key: string]: any } | string) => Promise<string>
|
||||
|
||||
export interface AuthorizeOptions {
|
||||
secret: string | SecretCallback
|
||||
algorithms?: Algorithm[]
|
||||
}
|
||||
|
||||
export const authorize = (options: AuthorizeOptions): SocketIOMiddleware => {
|
||||
const { secret, algorithms = ['HS256'] } = options
|
||||
return (socket, next) => {
|
||||
let token: string | null = null
|
||||
return async (socket, next) => {
|
||||
let encodedToken: string | null = null
|
||||
const authorizationHeader = socket.request.headers.authorization
|
||||
if (authorizationHeader != null) {
|
||||
const tokenSplitted = authorizationHeader.split(' ')
|
||||
@ -40,9 +42,9 @@ export const authorize = (options: AuthorizeOptions): SocketIOMiddleware => {
|
||||
})
|
||||
)
|
||||
}
|
||||
token = tokenSplitted[1]
|
||||
encodedToken = tokenSplitted[1]
|
||||
}
|
||||
if (token == null) {
|
||||
if (encodedToken == null) {
|
||||
return next(
|
||||
new UnauthorizedError('credentials_required', {
|
||||
message: 'no token provided'
|
||||
@ -50,10 +52,17 @@ export const authorize = (options: AuthorizeOptions): SocketIOMiddleware => {
|
||||
)
|
||||
}
|
||||
// Store encoded JWT
|
||||
socket.encodedToken = token
|
||||
let payload: any
|
||||
socket.encodedToken = encodedToken
|
||||
let keySecret: string | null = null
|
||||
let decodedToken: any
|
||||
if (typeof secret === 'string') {
|
||||
keySecret = secret
|
||||
} else {
|
||||
decodedToken = jwt.decode(encodedToken, { complete: true })
|
||||
keySecret = await secret(decodedToken)
|
||||
}
|
||||
try {
|
||||
payload = jwt.verify(token, secret, { algorithms })
|
||||
decodedToken = jwt.verify(encodedToken, keySecret, { algorithms })
|
||||
} catch {
|
||||
return next(
|
||||
new UnauthorizedError('invalid_token', {
|
||||
@ -62,7 +71,7 @@ export const authorize = (options: AuthorizeOptions): SocketIOMiddleware => {
|
||||
)
|
||||
}
|
||||
// Store decoded JWT
|
||||
socket.decodedToken = payload
|
||||
socket.decodedToken = decodedToken
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user