Skip to content

Commit

Permalink
feat(oauth): support optional scope in OAuth request (#25)
Browse files Browse the repository at this point in the history
set CAMUNDA_TOKEN_SCOPE in the environment to pass a scope in the OAuth request
  • Loading branch information
jwulf authored Mar 5, 2024
1 parent 240d79a commit 0451b80
Show file tree
Hide file tree
Showing 10 changed files with 329 additions and 64 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Camunda 8 JavaScript SDK

This is the monorepo for the official Camunda 8 JavaScript SDK.
This is the official Camunda 8 JavaScript SDK.

## Using the SDK in your project

Install the SDK as a dependency:

```bash
npm i @camunda/sdk
npm i @camunda8/sdk
```
83 changes: 82 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
"build": "npm run clean && npm run compile",
"clean": "rm -rf ./dist && rm -f ./tsconfig.tsbuildinfo",
"compile": "tsc --project tsconfig.json",
"docs": "rm -rf ./docs && typedoc src/index.ts",
"test": "jest --detectOpenHandles --testPathIgnorePatterns integration local-integration disconnection",
"test:integration": "jest --runInBand --testPathIgnorePatterns disconnection --detectOpenHandles --verbose true",
"test:local": "jest --runInBand --verbose true --detectOpenHandles local-integration",
"test:local-integration": "jest --runInBand --detectOpenHandles --verbose --testPathIgnorePatterns disconnection --testPathIgnorePatterns console --testPathIgnorePatterns modeler",
"test:docker": "jest --runInBand --testPathIgnorePatterns disconnection local-integration --detectOpenHandles --verbose true",
"test:disconnect": "jest --runInBand --verbose true --detectOpenHandles disconnection",
"test&docs": "npm test && npm run docs",
"publish": "lerna run build && lerna run test && lerna publish",
"publish": "npm run build && npm run test && lerna publish",
"commit": "cz",
"publish:canary": "lerna run build && lerna run test && lerna publish --canary",
"publish:canary": "npm run build && npm run test && lerna publish --canary",
"prepare": "husky install",
"prepublishOnly": "npm run build",
"lint": "eslint 'src/**/*.{ts,tsx}'",
Expand Down Expand Up @@ -93,6 +94,7 @@
"semantic-release": "^22.0.12",
"semantic-release-lerna": "^2.1.0",
"ts-jest": "^29.1.1",
"typedoc": "^0.25.9",
"typescript": "^5.3.3"
},
"dependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"token":"something","audience":"CONSOLE","expiry":null}
209 changes: 209 additions & 0 deletions src/oauth/__test__/OAuthImpl.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,118 @@
import fs from 'fs'
import http from 'http'
import path from 'path'

import { OAuthProviderImpl } from '../lib/OAuthProviderImpl'

jest.setTimeout(10000)

const STORED_ENV = {}
const ENV_VARS_TO_STORE = [
'CAMUNDA_TOKEN_CACHE_DIR',
'CAMUNDA_TOKEN_CACHE',
'CAMUNDA_TOKEN_SCOPE',
]

beforeAll(() => {
ENV_VARS_TO_STORE.forEach((e) => {
STORED_ENV[e] = process.env[e]
delete process.env[e]
})
})

afterAll(() => {
ENV_VARS_TO_STORE.forEach((e) => {
delete process.env[e]
if (STORED_ENV[e]) {
process.env[e] = STORED_ENV[e]
}
})
})

test('Gets the token cache dir from the environment', () => {
const tokenCacheDir = path.join(__dirname, '.token-cache')
if (fs.existsSync(tokenCacheDir)) {
fs.rmSync(tokenCacheDir, {
recursive: true,
force: true,
})
}
expect(fs.existsSync(tokenCacheDir)).toBe(false)
process.env.CAMUNDA_TOKEN_CACHE_DIR = tokenCacheDir
const o = new OAuthProviderImpl({
audience: 'token',
clientId: 'clientId',
clientSecret: 'clientSecret',
authServerUrl: 'url',
userAgentString: 'test',
})
expect(o).toBeTruthy()
expect(fs.existsSync(tokenCacheDir)).toBe(true)
if (fs.existsSync(tokenCacheDir)) {
fs.rmdirSync(tokenCacheDir)
}
expect(fs.existsSync(tokenCacheDir)).toBe(false)
})

test('Creates the token cache dir if it does not exist', () => {
process.env.CAMUNDA_TOKEN_CACHE_DIR = path.join(process.cwd(), '.token-cache')
const tokenCacheDir = OAuthProviderImpl.getTokenCacheDirFromEnv()
if (fs.existsSync(tokenCacheDir)) {
fs.rmSync(tokenCacheDir, {
recursive: true,
force: true,
})
}
expect(fs.existsSync(tokenCacheDir)).toBe(false)

const o = new OAuthProviderImpl({
audience: 'token',
clientId: 'clientId',
clientSecret: 'clientSecret',
authServerUrl: 'url',
userAgentString: 'test',
})
expect(o).toBeTruthy()
expect(fs.existsSync(tokenCacheDir)).toBe(true)
if (fs.existsSync(tokenCacheDir)) {
fs.rmdirSync(tokenCacheDir)
}
expect(fs.existsSync(tokenCacheDir)).toBe(false)
})

test('Throws in the constructor if the token cache is not writable', () => {
const tokenCacheDir = path.join(__dirname, '.token-cache')
process.env.CAMUNDA_TOKEN_CACHE_DIR = tokenCacheDir
if (fs.existsSync(tokenCacheDir)) {
fs.rmSync(tokenCacheDir, {
recursive: true,
force: true,
})
}
expect(fs.existsSync(tokenCacheDir)).toBe(false)
fs.mkdirSync(tokenCacheDir, 0o400)
expect(fs.existsSync(tokenCacheDir)).toBe(true)
let thrown = false
try {
const o = new OAuthProviderImpl({
audience: 'token',
clientId: 'clientId',
// file deepcode ignore HardcodedNonCryptoSecret/test: <please specify a reason of ignoring this>
clientSecret: 'clientSecret',
authServerUrl: 'url',
userAgentString: 'test',
})
expect(o).toBeTruthy()
} catch {
thrown = true
}
expect(thrown).toBe(true)
if (fs.existsSync(tokenCacheDir)) {
fs.rmdirSync(tokenCacheDir)
}
expect(fs.existsSync(tokenCacheDir)).toBe(false)
})

// Added test for https://github.com/camunda-community-hub/camunda-saas-oauth-nodejs/issues/8
// "Can not renew expired token"
// Updated test for https://github.com/camunda-community-hub/camunda-8-js-sdk/issues/3
Expand Down Expand Up @@ -52,3 +162,102 @@ test('In-memory cache is populated and evicted after timeout', (done) => {
server.close(() => done())
})
})

test('Uses form encoding for request', (done) => {
const o = new OAuthProviderImpl({
audience: 'token',
clientId: 'clientId',
clientSecret: 'clientSecret',
authServerUrl: 'http://127.0.0.1:3001',
userAgentString: 'test',
})
const server = http
.createServer((req, res) => {
if (req.method === 'POST') {
let body = ''
req.on('data', (chunk) => {
body += chunk
})

req.on('end', () => {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end('{"token": "something"}')
server.close()
expect(body).toEqual(
'audience=token&client_id=clientId&client_secret=clientSecret&grant_type=client_credentials'
)
done()
})
}
})
.listen(3001)
o.getToken('CONSOLE').finally(() => server.close())
})

test('Passes scope, if provided', () => {
const o = new OAuthProviderImpl({
audience: 'token',
scope: 'scope',
clientId: 'clientId',
clientSecret: 'clientSecret',
authServerUrl: 'http://127.0.0.1:3002',
userAgentString: 'test',
})
const server = http
.createServer((req, res) => {
if (req.method === 'POST') {
let body = ''
req.on('data', (chunk) => {
body += chunk
})

req.on('end', () => {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end('{"token": "something"}')

expect(body).toEqual(
'audience=token&client_id=clientId&client_secret=clientSecret&grant_type=client_credentials&scope=scope'
)
})
}
})
.listen(3002)

return o.getToken('CONSOLE').finally(() => {
return server.close()
})
})

test('Can get scope from environment', () => {
process.env.CAMUNDA_TOKEN_SCOPE = 'scope2'
const o = new OAuthProviderImpl({
audience: 'token',
clientId: 'clientId',
clientSecret: 'clientSecret',
authServerUrl: 'http://127.0.0.1:3003',
userAgentString: 'test',
})
const server = http
.createServer((req, res) => {
if (req.method === 'POST') {
let body = ''
req.on('data', (chunk) => {
body += chunk
})

req.on('end', () => {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end('{"token": "something"}')

expect(body).toEqual(
'audience=token&client_id=clientId&client_secret=clientSecret&grant_type=client_credentials&scope=scope2'
)
})
}
})
.listen(3003)

return o.getToken('CONSOLE').finally(() => {
return server.close()
})
})
Loading

0 comments on commit 0451b80

Please sign in to comment.