Created
December 19, 2017 11:01
-
-
Save vgw-rhysc/b7c82454db671a25c2ad548996581aa5 to your computer and use it in GitHub Desktop.
Sample SPA app that authenticates with OKAT - replace "THE CLIENT ID HERE"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const express = require('express'); | |
const OktaJwtVerifier = require('@okta/jwt-verifier'); | |
var cors = require('cors'); | |
var oktaApplicationClientId = "THE CLIENT ID HERE" | |
const oktaJwtVerifier = new OktaJwtVerifier({ | |
issuer: 'https://vgw.okta.com/oauth2/ausfg73gzLfAb6cKg2p6', //Auth server | |
assertClaims: { | |
cid: oktaApplicationClientId, | |
} | |
}); | |
/** | |
* THIS IS A MIDDLEWARE FUNCTION!!!! | |
* Asserts valid access tokens and sends 401 responses if the token is not present or fails validation. | |
* If the token is valid its contents are attached to req.jwt | |
**/ | |
function authenticationRequired(req, res, next) { | |
const authHeader = req.headers.authorization || ''; | |
const match = authHeader.match(/Bearer (.+)/); | |
if (!match) { | |
console.error("No Bearer header found"); | |
return res.status(401).end(); | |
} | |
const accessToken = match[1]; | |
return oktaJwtVerifier.verifyAccessToken(accessToken) //This will make a call to the OKTA auth server to validate the signing key (JWT header kid) | |
.then((jwt) => { | |
req.jwt = jwt; | |
next(); | |
}) | |
.catch((err) => { | |
console.log(err); | |
res.status(401).send(err.message); | |
}); | |
} | |
/* END custom middleware */ | |
const app = express(); | |
// Allow serving of static content | |
app.use(express.static('public')) | |
app.use('/', express.static('./public/index.html')); | |
// For local testing only! Enables CORS for all domains | |
app.use(cors()); | |
// Example route that requires a valid access token for authentication, and print some messages for the user if they are authenticated | |
app.get('/api/secure/hello', authenticationRequired, (req, res) => { | |
res.json([{ | |
message: 'Hello, ' + req.jwt.claims.sub + '!' | |
}]); | |
}); | |
// Another example route that does *not* require a valid access token for authentication | |
app.get('/api/hello', (req, res) => { | |
res.json([{ | |
message: 'Hello, world!' | |
}]); | |
}); | |
//start the web server | |
app.listen(3000, () => { | |
console.log('We are running - go to http://localhost:3000/'); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "oktaauth", | |
"version": "1.0.0", | |
"description": "SPA demo of Okta with an express back end to show authenticated request using OAuth2 implicit flow and acess token verification using the Okta auth server", | |
"main": "app.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"author": "RhysC", | |
"license": "ISC", | |
"dependencies": { | |
"@okta/jwt-verifier": "0.0.2", | |
"cors": "^2.8.4", | |
"express": "^4.16.2" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<!-- DOCS HERE: https://developer.okta.com/code/javascript/okta_auth_sdk?_ga=1.175804903.493946744.1506927708 --> | |
<!-- and here: https://developer.okta.com/code/javascript/okta_auth_sdk_ref --> | |
<script | |
src="https://code.jquery.com/jquery-3.2.1.min.js" | |
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" | |
crossorigin="anonymous"></script> | |
<script src="https://ok1static.oktacdn.com/assets/js/sdk/okta-auth-js/1.8.0/okta-auth-js.min.js" type="text/javascript"></script> | |
</head> | |
<body> | |
<div id="greeting"> | |
Hi, please login via the OKTA console: | |
</div> | |
<strong>HINT:</strong> <small>Look in the dev console to see what is going on behind the scenes :) </small> | |
<br/> | |
<div id="apiCallButtons"> | |
<button id="btnCallApi">Call non secured Hello API endpoint</button> | |
<br/> | |
<hr/> | |
<button id="btnCallSecureApi">Call Secure Hello API endpoint</button> | |
</div> | |
<div id="output"> | |
</div> | |
<script type="text/javascript"> | |
let greetUser = function(idToken){ | |
let greetingText = `Hi ${idToken.claims.email}!`; | |
console.log(greetingText); | |
document.getElementById("greeting").innerText = greetingText | |
} | |
//OKTA LOGIN FLOW | |
var clientId = "THE CLIENT ID HERE", | |
var authClient = new OktaAuth({ | |
url: "https://vgw.okta.com", | |
clientId: clientId, | |
redirectUri: 'http://localhost:3000/' | |
}); | |
var idToken = authClient.tokenManager.get('idToken'); | |
if (idToken) { | |
greetUser(idToken); | |
} | |
else if (location.hash) { | |
console.log("We have a URL hash, lets get the tokens from it..."); | |
authClient.token.parseFromUrl() | |
.then(parameters => { | |
var accessToken = parameters[0]; | |
var idToken = parameters[1]; | |
greetUser(idToken); | |
// Store parsed token in Token Manager | |
authClient.tokenManager.add('idToken', idToken); | |
authClient.tokenManager.add('accessToken', accessToken); | |
console.log(idToken); | |
console.log(accessToken); | |
}) | |
.catch(err=>{ | |
console.error(err); | |
}); | |
} | |
else { | |
document.getElementById("greeting").innerText = "Redirecting you to Okta to sign in..."; | |
authClient.token.getWithRedirect({ | |
responseType: ['token', 'id_token'], | |
scopes: ['openid', 'email', 'profile'] | |
}); | |
} | |
// END OKTA LOGIN FLOW | |
//BEGIN - API back end integration - ie our secure and non secure REST calls | |
var showOutput = function(response, isError=false) { | |
var preTag = document.createElement('pre'); | |
preTag.innerText = JSON.stringify(response) | |
if(isError){ | |
preTag.style = "color:red" | |
} | |
document.getElementById("output").appendChild(preTag); | |
} | |
function callMessagesApi(url) { | |
var accessToken = authClient.tokenManager.get('accessToken'); | |
if (!accessToken) { | |
console.error("Request will not be authenticated, No access token found. Sending request anyway #YOLO"); | |
accessToken = { accessToken: '' }; | |
} | |
$.ajax({ | |
url: url, | |
headers: { | |
Authorization : 'Bearer ' + accessToken.accessToken | |
}, | |
success: function(response) { | |
console.log('Response', response); | |
showOutput(response); | |
}, | |
error: function(response) { | |
console.error(response); | |
showOutput(response, true); | |
} | |
}); | |
} | |
document.getElementById("btnCallApi").onclick = function(){ callMessagesApi('http://localhost:3000/api/hello')}; | |
document.getElementById("btnCallSecureApi").onclick = function(){ callMessagesApi('http://localhost:3000/api/secure/hello')}; | |
//END - API back end integration | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment