Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb8deb221e | |||
| a465ae543a | |||
| 8aad0d6308 | |||
| 0166537d2f | |||
| 9f6e63ea50 | |||
| a30cb4b08c | |||
| abbc55ea6e | |||
| 779d78cdc6 | |||
| cf958ffa85 | |||
| eb579289f0 | |||
| d1ff4ec278 | |||
| 14c2c8f366 | |||
| 7177ba9516 | |||
| 652483e2de | |||
| 4e43361217 | |||
| 6dda660e78 | |||
| 534772b315 | |||
| 3bdfe7bf0e | |||
| edba76d0ab | |||
| 5289b4fa23 | |||
| eef7168f37 | |||
| 596967ff72 | |||
| 5f2385a92f | |||
| a5653c8ea6 | |||
| 75ec899c99 | |||
| cb31381be2 | |||
| 9ee344311a | |||
| d7e3b10e80 | |||
| 7b306dd500 | |||
| 22d096a2be | |||
| 858cb96e10 | |||
| e8dd55208c | |||
| dbf5206c1b | |||
| 4229508bba | |||
| b4d5dbe9e3 | |||
| b476cf0e36 |
+1
-1
@@ -16,7 +16,7 @@ image: buildtool/build-tools:${BUILDTOOLS_VERSION}
|
||||
build:
|
||||
stage: build
|
||||
services:
|
||||
- docker:dind
|
||||
- docker:${DOCKER_DIND_VERSION}
|
||||
script:
|
||||
- build
|
||||
- push
|
||||
|
||||
@@ -10,8 +10,10 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 20
|
||||
rebase-strategy: none
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 20
|
||||
rebase-strategy: none
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
*.yaml
|
||||
*.yml
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"arrowParens": "always",
|
||||
"quoteProps": "as-needed",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:18
|
||||
FROM node:20
|
||||
ENV AUDIENCE "https://shiny.unbound.se"
|
||||
ENV ORIGIN_HOST "auth0mock"
|
||||
ENV ORIGIN "https://auth0mock:3333"
|
||||
|
||||
@@ -3,47 +3,52 @@
|
||||
> This server helps you to simulate auth0 server locally. So, you are able to use the `/tokeninfo` endpoint to verify your token.
|
||||
|
||||
## Getting Started
|
||||
### Prerequisites
|
||||
* Install [Node.js](http://nodejs.org)
|
||||
* on OSX use [homebrew](http://brew.sh) `brew install node`
|
||||
* on Windows use [chocolatey](https://chocolatey.org/) `choco install nodejs`
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Install [Node.js](http://nodejs.org)
|
||||
- on OSX use [homebrew](http://brew.sh) `brew install node`
|
||||
- on Windows use [chocolatey](https://chocolatey.org/) `choco install nodejs`
|
||||
|
||||
## Installing
|
||||
* `fork` this repo
|
||||
* `clone` your fork
|
||||
* `npm install` to install all dependencies
|
||||
|
||||
- `fork` this repo
|
||||
- `clone` your fork
|
||||
- `npm install` to install all dependencies
|
||||
|
||||
## Running the app
|
||||
|
||||
After you have installed all dependencies you can now run the app.
|
||||
Run `npm start` to start a local server.
|
||||
The port will be displayed to you as `http://0.0.0.0:3333` (or if you prefer IPv6, if you're using `express` server, then it's `http://[::1]:3333/`).
|
||||
|
||||
|
||||
## API Documentation
|
||||
|
||||
### `GET` /token/:username
|
||||
|
||||
Returns a token with the given user(username). This token can the be used by your application.
|
||||
|
||||
### `POST` /tokeninfo
|
||||
|
||||
Returns the data of the token like the username.
|
||||
|
||||
**Body**
|
||||
|
||||
```
|
||||
{
|
||||
"id_token": "your-token-kjasdf6ashasl..."
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Related Projects
|
||||
* [express-typescript-boilerplate](https://github.com/w3tecch/express-typescript-boilerplate) - Boilerplate for an restful express-apllication written in TypeScript
|
||||
* [express-graphql-typescript-boilerplate](https://github.com/w3tecch/express-graphql-typescript-boilerplate) - A starter kit for building amazing GraphQL API's with TypeScript and express by @w3tecch
|
||||
|
||||
- [express-typescript-boilerplate](https://github.com/w3tecch/express-typescript-boilerplate) - Boilerplate for an restful express-apllication written in TypeScript
|
||||
- [express-graphql-typescript-boilerplate](https://github.com/w3tecch/express-graphql-typescript-boilerplate) - A starter kit for building amazing GraphQL API's with TypeScript and express by @w3tecch
|
||||
|
||||
## License
|
||||
|
||||
[MIT](/LICENSE)
|
||||
|
||||
|
||||
---
|
||||
Made with ♥ by Gery Hirschfeld ([@GeryHirschfeld1](https://twitter.com/GeryHirschfeld1))
|
||||
|
||||
Made with ♥ by Gery Hirschfeld ([@GeryHirschfeld1](https://twitter.com/GeryHirschfeld1))
|
||||
|
||||
@@ -14,8 +14,10 @@ const cert = require('./cert')
|
||||
let issuer = process.env.ISSUER || 'localhost:3333'
|
||||
let jwksOrigin = `https://${issuer}/`
|
||||
const audience = process.env.AUDIENCE || 'https://generic-audience'
|
||||
const adminCustomClaim = process.env.ADMIN_CUSTOM_CLAIM || 'https://unbound.se/admin'
|
||||
const emailCustomClaim = process.env.EMAIL_CUSTOM_CLAIM || 'https://unbound.se/email'
|
||||
const adminCustomClaim =
|
||||
process.env.ADMIN_CUSTOM_CLAIM || 'https://unbound.se/admin'
|
||||
const emailCustomClaim =
|
||||
process.env.EMAIL_CUSTOM_CLAIM || 'https://unbound.se/email'
|
||||
|
||||
const debug = Debug('app')
|
||||
|
||||
@@ -37,13 +39,20 @@ const addCustomClaims = (email, customClaims, token) => {
|
||||
...claim
|
||||
}
|
||||
}, token)
|
||||
}
|
||||
|
||||
const signToken = (token) => {
|
||||
return jwt.sign(Buffer.from(JSON.stringify(token)), privateKey, {
|
||||
algorithm: 'RS256',
|
||||
keyid: thumbprint
|
||||
})
|
||||
}
|
||||
|
||||
// Configure our small auth0-mock-server
|
||||
app.options('*', cors(corsOpts))
|
||||
app
|
||||
.options('*', cors(corsOpts))
|
||||
.use(cors())
|
||||
.use(bodyParser.json())
|
||||
.use(bodyParser.json({ strict: false }))
|
||||
.use(bodyParser.urlencoded({ extended: true }))
|
||||
.use(cookieParser())
|
||||
.use(express.static(`${__dirname}/public`))
|
||||
@@ -51,47 +60,81 @@ app.options('*', cors(corsOpts))
|
||||
|
||||
// This route can be used to generate a valid jwt-token.
|
||||
app.post('/oauth/token', (req, res) => {
|
||||
const code = req.body.code
|
||||
const session = sessions[code]
|
||||
|
||||
let date = Math.floor(Date.now() / 1000)
|
||||
let accessToken = jwt.sign(Buffer.from(JSON.stringify(addCustomClaims(session.email, session.customClaims, {
|
||||
iss: jwksOrigin,
|
||||
aud: [audience],
|
||||
sub: 'auth0|' + session.email,
|
||||
iat: date,
|
||||
exp: date + 7200,
|
||||
azp: session.clientId
|
||||
}))), privateKey, {
|
||||
algorithm: 'RS256',
|
||||
keyid: thumbprint
|
||||
})
|
||||
if (req.body.grant_type === 'client_credentials' && req.body.client_id) {
|
||||
let accessToken = signToken({
|
||||
iss: jwksOrigin,
|
||||
aud: [audience],
|
||||
sub: 'auth0|management',
|
||||
iat: date,
|
||||
exp: date + 7200,
|
||||
azp: req.body.client_id
|
||||
})
|
||||
|
||||
let idToken = jwt.sign(Buffer.from(JSON.stringify(addCustomClaims(session.email, session.customClaims, {
|
||||
iss: jwksOrigin,
|
||||
aud: session.clientId,
|
||||
nonce: session.nonce,
|
||||
sub: 'auth0|' + session.email,
|
||||
iat: date,
|
||||
exp: date + 7200,
|
||||
azp: session.clientId,
|
||||
name: 'Example Person',
|
||||
picture: 'https://cdn.playbuzz.com/cdn/5458360f-32ea-460e-a707-1a2d26760558/70bda687-cb84-4756-8a44-8cf735ed87b3.jpg'
|
||||
}))), privateKey, {
|
||||
algorithm: 'RS256',
|
||||
keyid: thumbprint
|
||||
})
|
||||
let idToken = signToken({
|
||||
iss: jwksOrigin,
|
||||
aud: req.body.client_id,
|
||||
sub: 'auth0|management',
|
||||
iat: date,
|
||||
exp: date + 7200,
|
||||
azp: req.body.client_id,
|
||||
name: 'Management API'
|
||||
})
|
||||
|
||||
debug('Signed token for ' + session.email)
|
||||
// res.json({ token });
|
||||
debug('Signed token for management API')
|
||||
|
||||
res.json({
|
||||
access_token: accessToken,
|
||||
id_token: idToken,
|
||||
scope: 'openid%20profile%20email',
|
||||
expires_in: 7200,
|
||||
token_type: 'Bearer'
|
||||
})
|
||||
res.json({
|
||||
access_token: accessToken,
|
||||
id_token: idToken,
|
||||
scope: 'openid%20profile%20email',
|
||||
expires_in: 7200,
|
||||
token_type: 'Bearer'
|
||||
})
|
||||
} else if (req.body.code) {
|
||||
const code = req.body.code
|
||||
const session = sessions[code]
|
||||
let accessToken = signToken(
|
||||
addCustomClaims(session.email, session.customClaims, {
|
||||
iss: jwksOrigin,
|
||||
aud: [audience],
|
||||
sub: 'auth0|' + session.email,
|
||||
iat: date,
|
||||
exp: date + 7200,
|
||||
azp: session.clientId
|
||||
})
|
||||
)
|
||||
|
||||
let idToken = signToken(
|
||||
addCustomClaims(session.email, session.customClaims, {
|
||||
iss: jwksOrigin,
|
||||
aud: session.clientId,
|
||||
nonce: session.nonce,
|
||||
sub: 'auth0|' + session.email,
|
||||
iat: date,
|
||||
exp: date + 7200,
|
||||
azp: session.clientId,
|
||||
name: 'Example Person',
|
||||
given_name: 'Example',
|
||||
family_name: 'Person',
|
||||
email: session.email,
|
||||
picture:
|
||||
'https://cdn.playbuzz.com/cdn/5458360f-32ea-460e-a707-1a2d26760558/70bda687-cb84-4756-8a44-8cf735ed87b3.jpg'
|
||||
})
|
||||
)
|
||||
|
||||
debug('Signed token for ' + session.email)
|
||||
|
||||
res.json({
|
||||
access_token: accessToken,
|
||||
id_token: idToken,
|
||||
scope: 'openid%20profile%20email',
|
||||
expires_in: 7200,
|
||||
token_type: 'Bearer'
|
||||
})
|
||||
} else {
|
||||
res.status(401)
|
||||
res.send('Missing client_id or client_secret')
|
||||
}
|
||||
})
|
||||
|
||||
// This route can be used to generate a valid jwt-token.
|
||||
@@ -100,9 +143,12 @@ app.get('/token/:email', (req, res) => {
|
||||
debug('No user was given!')
|
||||
return res.status(400).send('user is missing')
|
||||
}
|
||||
const token = jwt.sign({
|
||||
user_id: 'auth0|' + req.params.email
|
||||
}, privateKey)
|
||||
const token = jwt.sign(
|
||||
{
|
||||
user_id: 'auth0|' + req.params.email
|
||||
},
|
||||
privateKey
|
||||
)
|
||||
debug('Signed token for ' + req.params.email)
|
||||
res.json({ token })
|
||||
})
|
||||
@@ -127,7 +173,9 @@ app.post('/code', (req, res) => {
|
||||
codeChallenge: req.body.codeChallenge,
|
||||
customClaims: [claim]
|
||||
}
|
||||
res.redirect(`${req.body.redirect}?domain=${issuer}&code=${code}&state=${encodeURIComponent(state)}`)
|
||||
res.redirect(
|
||||
`${req.body.redirect}?code=${code}&state=${encodeURIComponent(state)}`
|
||||
)
|
||||
})
|
||||
|
||||
app.get('/authorize', (req, res) => {
|
||||
@@ -138,13 +186,26 @@ app.get('/authorize', (req, res) => {
|
||||
const codeChallenge = req.query.code_challenge
|
||||
const prompt = req.query.prompt
|
||||
const responseMode = req.query.response_mode
|
||||
if (responseMode === 'query') {
|
||||
const code = req.cookies['auth0']
|
||||
const session = sessions[code]
|
||||
if (session) {
|
||||
session.nonce = nonce
|
||||
session.state = state
|
||||
session.codeChallenge = codeChallenge
|
||||
sessions[codeChallenge] = session
|
||||
res.redirect(`${redirect}?code=${codeChallenge}&state=${state}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (prompt === 'none' && responseMode === 'web_message') {
|
||||
const code = req.cookies['auth0']
|
||||
const session = sessions[code]
|
||||
session.nonce = nonce
|
||||
session.state = state
|
||||
session.codeChallenge = codeChallenge
|
||||
res.send(`
|
||||
if (session) {
|
||||
session.nonce = nonce
|
||||
session.state = state
|
||||
session.codeChallenge = codeChallenge
|
||||
res.send(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
@@ -162,13 +223,16 @@ app.get('/authorize', (req, res) => {
|
||||
</script>
|
||||
</body>
|
||||
</html>`)
|
||||
} else {
|
||||
res.cookie('auth0', codeChallenge, {
|
||||
sameSite: 'None',
|
||||
secure: true,
|
||||
httpOnly: true
|
||||
})
|
||||
res.send(`
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res.cookie('auth0', codeChallenge, {
|
||||
sameSite: 'None',
|
||||
secure: true,
|
||||
httpOnly: true
|
||||
})
|
||||
res.send(`
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
@@ -209,21 +273,29 @@ app.get('/authorize', (req, res) => {
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/userinfo', (req, res) => {
|
||||
res.contentType('application/json').send(JSON.stringify({ picture: 'https://cdn.playbuzz.com/cdn/5458360f-32ea-460e-a707-1a2d26760558/70bda687-cb84-4756-8a44-8cf735ed87b3.jpg' }))
|
||||
res.contentType('application/json').send(
|
||||
JSON.stringify({
|
||||
picture:
|
||||
'https://cdn.playbuzz.com/cdn/5458360f-32ea-460e-a707-1a2d26760558/70bda687-cb84-4756-8a44-8cf735ed87b3.jpg'
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
app.get('/v2/logout', (req, res) => {
|
||||
res.redirect(`${req.query.returnTo}?domain=${issuer}`)
|
||||
const code = req.cookies['auth0']
|
||||
const session = sessions[code]
|
||||
if (session) {
|
||||
delete sessions[code]
|
||||
}
|
||||
res.redirect(req.query.returnTo)
|
||||
})
|
||||
|
||||
app.get('/.well-known/jwks.json', (req, res) => {
|
||||
res
|
||||
.contentType('application/json')
|
||||
.send(JSON.stringify({
|
||||
res.contentType('application/json').send(
|
||||
JSON.stringify({
|
||||
keys: [
|
||||
{
|
||||
alg: 'RS256',
|
||||
@@ -237,7 +309,8 @@ app.get('/.well-known/jwks.json', (req, res) => {
|
||||
x5t: thumbprint
|
||||
}
|
||||
]
|
||||
}))
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
// This route returns the inside of a jwt-token. Your main application
|
||||
@@ -264,7 +337,13 @@ app.post('/issuer', (req, res) => {
|
||||
}
|
||||
issuer = req.body.issuer
|
||||
jwksOrigin = `https://${issuer}/`
|
||||
const { privateKey: key, certDer: der, thumbPrint: thumb, exponent: exp, modulus: mod } = cert(jwksOrigin)
|
||||
const {
|
||||
privateKey: key,
|
||||
certDer: der,
|
||||
thumbprint: thumb,
|
||||
exponent: exp,
|
||||
modulus: mod
|
||||
} = cert(jwksOrigin)
|
||||
privateKey = key
|
||||
certDer = der
|
||||
thumbprint = thumb
|
||||
@@ -274,6 +353,28 @@ app.post('/issuer', (req, res) => {
|
||||
res.send('ok')
|
||||
})
|
||||
|
||||
app.get('/api/v2/users-by-email', (req, res) => {
|
||||
res.json([])
|
||||
})
|
||||
|
||||
app.post('/api/v2/users', (req, res) => {
|
||||
const email = req.body.email
|
||||
res.json({
|
||||
user_id: `auth0|${email}`
|
||||
})
|
||||
})
|
||||
|
||||
app.post('/api/v2/tickets/password-change', (req, res) => {
|
||||
res.json({
|
||||
ticket: `https://some-url`
|
||||
})
|
||||
})
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
console.log('404', req.path)
|
||||
res.status(404).send('error: 404 Not Found ' + req.path)
|
||||
})
|
||||
|
||||
app.listen(3333, () => {
|
||||
debug('Auth0-Mock-Server listening on port 3333!')
|
||||
})
|
||||
|
||||
@@ -43,11 +43,7 @@ const PUBLIC_KEY_PEM =
|
||||
'HwIDAQAB\n' +
|
||||
'-----END PUBLIC KEY-----\n'
|
||||
|
||||
const createCertificate = ({
|
||||
publicKey,
|
||||
privateKey,
|
||||
jwksOrigin
|
||||
}) => {
|
||||
const createCertificate = ({ publicKey, privateKey, jwksOrigin }) => {
|
||||
const cert = forge.pki.createCertificate()
|
||||
cert.publicKey = publicKey
|
||||
cert.serialNumber = '123'
|
||||
@@ -122,8 +118,8 @@ const setup = (jwksOrigin) => {
|
||||
|
||||
return {
|
||||
privateKey: forge.pki.privateKeyToPem(privateKey),
|
||||
certDer: certDer,
|
||||
thumbPrint: thumbprint.toString(),
|
||||
certDer,
|
||||
thumbprint: thumbprint.toString(),
|
||||
exponent: bnToB64(exponent),
|
||||
modulus: modulus.toString('base64')
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: auth0mock
|
||||
image: registry.gitlab.com/unboundsoftware/shiny/auth0mock:${COMMIT}
|
||||
image: registry.gitlab.com/unboundsoftware/auth0mock:${COMMIT}
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
resources:
|
||||
requests:
|
||||
|
||||
+11
-5
@@ -6,23 +6,29 @@
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "nodemon ./app.js",
|
||||
"start": "node ./app.js"
|
||||
"start": "node ./app.js",
|
||||
"lint:prettier": "prettier --check .",
|
||||
"lint": "yarn lint:prettier",
|
||||
"lintfix": "prettier --write --list-different ."
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-url": "^2.3.3",
|
||||
"body-parser": "^1.20.0",
|
||||
"body-parser": "^1.20.2",
|
||||
"buffer": "^6.0.3",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.3",
|
||||
"debug": "^4.3.4",
|
||||
"express": "^4.18.0",
|
||||
"express": "^4.18.2",
|
||||
"https-localhost": "^4.7.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-rsa": "^1.1.1",
|
||||
"nodemon": "^2.0.15",
|
||||
"nodemon": "^2.0.22",
|
||||
"serve-favicon": "^2.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^2.8.8"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user