Skip to content

Commit e02a57b

Browse files
author
Katie Horne
committed
Add sample
0 parents  commit e02a57b

8 files changed

+303
-0
lines changed

.DS_Store

6 KB
Binary file not shown.

.env

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
AUTH0_DOMAIN="YOUR_AUTH0_DOMAIN"
2+
SPA_PORT=3000
3+
CONTACTS_API_PORT=3001
4+
CALENDAR_API_PORT=3002

README.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
## Overview
2+
3+
This is a sample application that demonstrates the usage of a single Resource Server with namespaced scoping representing multiple APIs. This sample consists of:
4+
5+
- 2 Node.js APIs: `contacts` and `calendar` (you can think of them as microservices);
6+
- 1 Resource Server representing the 2 APIs;
7+
- 2 Namespaced scopes: `read:contacts` and `read:calendar`;
8+
- The Implicit Grant flow to obtain an `access_token` that works for both APIs
9+
10+
## Setup
11+
12+
You will need to create an API using the Auth0 Dashboard called `Organizer Service` with the unique identifier `organize` (this is later used in the `audience` parameter of your Authorization URL).
13+
14+
The API needs two namespaced scopes:
15+
16+
* `read:contacts`
17+
* `read:calendar`
18+
19+
## Usage
20+
21+
Prior to beginning, you may need to make some or all of the following changes so that the sample runs on your local environment:
22+
23+
* `.env` (Please note that this file will, by default, be hidden):
24+
* replace the placeholder with your Auth0 Domain
25+
* update the ports you're using to serve your SPA and APIs
26+
* `calendar-api.js`: replace each of the two placeholders with your Auth0 Domain
27+
* `contacts-api.js`: replace each of the two placeholders with your Auth0 Domain
28+
* `index.html`:
29+
* update the Authorization URL with your Auth0 Client ID and the port you're using to serve the SPA
30+
* replace each of the two placeholders with the ports you're using to serve the `contacts` and `calendar` APIs
31+
32+
### Run the Sample
33+
34+
1. Navigate to the root of your sample folder.
35+
2. Run `npm install` to install the dependencies.
36+
3. Start the two Node.js APIs and the Node.js host for the SPA by running `npm run dev`

calendar-api.js

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const express = require('express');
2+
const app = express();
3+
const jwt = require('express-jwt');
4+
const jwksRsa = require('jwks-rsa');
5+
const cors = require('cors');
6+
7+
require('dotenv').config();
8+
9+
const port = process.env.CALENDAR_API_PORT;
10+
const domain = process.env.AUTH0_DOMAIN;
11+
12+
app.use(cors());
13+
14+
// Validate the access token and enable the use of the jwtCheck middleware
15+
app.use(jwt({
16+
// Dynamically provide a signing key based on the kid in the header
17+
// and the singing keys provided by the JWKS endpoint
18+
secret: jwksRsa.expressJwtSecret({
19+
cache: true,
20+
rateLimit: true,
21+
jwksRequestsPerMinute: 5,
22+
// Replace with your Auth0 Domain
23+
jwksUri: `https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json`
24+
}),
25+
26+
27+
// Validate the audience and the issuer
28+
audience: 'organize',
29+
// Replace with your Auth0 Domain
30+
issuer: `https://YOUR_AUTH0_DOMAIN/`,
31+
algorithms: [ 'RS256' ]
32+
}));
33+
34+
//middleware to check scopes
35+
const checkPermissions = function(req, res, next){
36+
switch(req.path){
37+
case '/api/appointments':{
38+
var permissions = ['read:calendar'];
39+
for(var i = 0; i < permissions.length; i++){
40+
if(req.user.scope.includes(permissions[i])){
41+
next();
42+
} else {
43+
res.status(403).send({message:'Forbidden'});
44+
}
45+
}
46+
break;
47+
}
48+
}
49+
}
50+
51+
app.use(checkPermissions);
52+
53+
app.get('/api/appointments', function (req, res) {
54+
res.send({ appointments: [
55+
{ title: "1 on 1", time: "Mon Nov 14 2016 14:30:00 GMT-0500 (EST)" },
56+
{ title: "All Hands", time: "Thurs Nov 14 2016 14:23:20 GMT-0500 (EST)" }
57+
] });
58+
});
59+
60+
app.listen(port, function () {
61+
console.log('Calendar API started on port: ' + port);
62+
});

contacts-api.js

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const express = require('express');
2+
const app = express();
3+
const jwt = require('express-jwt');
4+
const jwksRsa = require('jwks-rsa');
5+
const cors = require('cors');
6+
7+
require('dotenv').config();
8+
9+
const port = process.env.CONTACTS_API_PORT;
10+
const domain = process.env.AUTH0_DOMAIN;
11+
12+
app.use(cors());
13+
14+
// Validate the access token and enable the use of the jwtCheck middleware
15+
app.use(jwt({
16+
// Dynamically provide a signing key based on the kid in the header and
17+
// the singing keys provided by the JWKS endpoint
18+
secret: jwksRsa.expressJwtSecret({
19+
cache: true,
20+
rateLimit: true,
21+
jwksRequestsPerMinute: 5,
22+
// Replace with your Auth0 Domain
23+
jwksUri: `https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json`
24+
}),
25+
26+
27+
// Validate the audience and the issuer
28+
audience: 'organize',
29+
// Replace with your Auth0 Domain
30+
issuer: `https://YOUR_AUTH0_DOMAIN/`,
31+
algorithms: [ 'RS256' ]
32+
}));
33+
34+
// Middleware to check scopes
35+
const checkPermissions = function(req, res, next){
36+
switch(req.path){
37+
case '/api/contacts':{
38+
var permissions = ['read:contacts'];
39+
for(var i = 0; i < permissions.length; i++){
40+
if(req.user.scope.includes(permissions[i])){
41+
next();
42+
} else {
43+
res.status(403).send({message:'Forbidden'});
44+
}
45+
}
46+
break;
47+
}
48+
}
49+
}
50+
51+
app.use(checkPermissions);
52+
53+
54+
app.get('/api/contacts', function (req, res) {
55+
res.send({ contacts: [
56+
{ name: "Jane", email: "[email protected]" },
57+
{ name: "John", email: "[email protected]" }
58+
] });
59+
});
60+
61+
app.listen(port, function () {
62+
console.log('Contacts API started on port: ' + port);
63+
});

index.html

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Single Page Application</title>
6+
</head>
7+
<body>
8+
<!-- Edit this URL to reflect your Auth0 Client ID and the port you're using -->
9+
<a href="https://auth0user.auth0.com/authorize?scope=read:contacts%20read:calendar&audience=organize&response_type=id_token%20token&client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:3000&nonce=NONCE">
10+
Sign In
11+
</a>
12+
13+
<div id="app" style="display:none;">
14+
<button id="get-contacts">List Contacts</button>
15+
<button id="get-appointments">List Appointments</button>
16+
<div id="results"><pre></pre></div>
17+
</div>
18+
19+
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
20+
<script>
21+
function getParameterByName(name) {
22+
var match = RegExp('[#&]' + name + '=([^&]*)').exec(window.location.hash);
23+
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
24+
}
25+
26+
function getAccessToken() {
27+
return getParameterByName('access_token');
28+
}
29+
30+
function getIdToken() {
31+
return getParameterByName('id_token');
32+
}
33+
34+
$(function () {
35+
var access_token = getAccessToken();
36+
if (access_token) {
37+
$('#app').show();
38+
} else {
39+
return
40+
}
41+
42+
console.log('Your access token is ' + access_token)
43+
44+
// Use the access token to make API calls
45+
$('#get-appointments').on('click', function(e) {
46+
e.preventDefault();
47+
48+
// Change to the port you're using
49+
$.ajax({
50+
url: "http://localhost:3001/api/appointments",
51+
method: "GET",
52+
headers: { "Authorization": "Bearer " + access_token },
53+
success: function (data) {
54+
$('#results pre').text(JSON.stringify(data, null, 2))
55+
}
56+
});
57+
});
58+
59+
$('#get-contacts').on('click', function(e) {
60+
e.preventDefault();
61+
62+
// Change to the port you're using
63+
$.ajax({
64+
url: "http://localhost:3002/api/contacts",
65+
method: "GET",
66+
headers: { "Authorization": "Bearer " + access_token },
67+
success: function (data) {
68+
$('#results pre').text(JSON.stringify(data, null, 2))
69+
}
70+
});
71+
});
72+
73+
});
74+
</script>
75+
</body>
76+
</html>

package.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "api-auth-implicit",
3+
"version": "1.0.0",
4+
"description": "A sample application to demonstrate the usage of multiple APIs in Auth0",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"dev": "node contacts-api.js | node calendar-api.js | node spa-host.js"
9+
},
10+
"author": "Fady Abdel Malik <[email protected]>",
11+
"license": "MIT",
12+
"dependencies": {
13+
"auth0-api-jwt-rsa-validation": "0.0.1",
14+
"cors": "^2.8.1",
15+
"dotenv": "^2.0.0",
16+
"express": "^4.14.0",
17+
"express-jwt": "^5.1.0",
18+
"jwks-rsa": "^1.1.1"
19+
}
20+
}

spa-host.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
const express = require('express');
2+
const app = express();
3+
const path = require('path');
4+
const fs = require('fs');
5+
6+
require('dotenv').config();
7+
8+
const calendarApiPort = process.env.CALENDAR_API_PORT;
9+
const contactsApiPort = process.env.CONTACTS_API_PORT;
10+
const domain = process.env.AUTH0_DOMAIN;
11+
const port = process.env.SPA_PORT;
12+
13+
app.get('/', function (req, res) {
14+
let indexPath = path.join(__dirname + '/index.html');
15+
replaceWithConfig(indexPath, (err, index) => {
16+
if (err) { return res.send(404); }
17+
18+
res.send(index);
19+
});
20+
21+
});
22+
23+
app.listen(port, function () {
24+
console.log('SPA being served on port: ' + port);
25+
});
26+
27+
const replaceWithConfig = (filePath, cb) => {
28+
let file = fs.readFile(filePath, (err, data) => {
29+
30+
data = data.toString();
31+
32+
if (err) { return cb(err) }
33+
34+
['CALENDAR_API_PORT', 'CONTACTS_API_PORT', 'AUTH0_DOMAIN'].forEach((configParam) => {
35+
let placeholder = '${' + configParam + '}'
36+
data = data.replace(placeholder, process.env[configParam]);
37+
});
38+
39+
return cb(null, data);
40+
41+
})
42+
}

0 commit comments

Comments
 (0)