Reputation: 494
I have a front end VueJS application running on port 8080 and a NodeJS server on port 3000. Now I need to integrate a secondary app whose API I need to access on a button click from my VueJS application. My current server architecture is as follows, I need to know whether I need to create a new server for the Authorization server or I can integrate with port 3000?
If I need to create a new server Authorization server, how do I add it? what are the settings. The problem what I faced is, I tried integrating the Authorization server with my backend server with port 3000, I was able to use the resource server, however I was able to call add
API call only through redirect url as shown below (OAuth.js
):
router.get('/oauth', async (req: Request, res: Response)=>{
oAuthSession = new OAuth(config.oauthRequestTokenUri, config.oauthAccessTokenUri, config.clientKey, config.clientSecret,
config.oAuthVersion, config.authorizeCallbackUri, config.oAuthSignatureMethod, config.oAuthNonceSize, config.oAuthCustomHeaders);
......
}
router.get('/callback', async (req: Request, res: Response)=>{
tokens.verifier = req.query.oauth_verifier;
l('----- Callback - Verifier Received -----');
l(JSON.stringify(tokens, null, 2));
l('----- Requesting Access Token and Secret -----');
oAuthSession.getOAuthAccessToken(tokens.requestToken, tokens.requestTokenSecret, tokens.verifier, function (error, token, secret, results) {
tokens.accessToken = token;
tokens.accessTokenSecret = secret;
l('----- Access Token and Secret Received -----');
l('StatusCode => ' + results.statusCode);
l(JSON.stringify(tokens, null, 2));
oAuthSession.get(config.platformBaseUri, tokens.accessToken, tokens.accessTokenSecret, function (error, responseData, result) {
l('StatusCode => ' + result.statusCode);
l('----- Ready to do OAuth authenticated calls now-----');
res.redirect(`http://localhost:3000/auth/add?accessToken=${tokens.accessToken}&accessTokenSecret=${tokens.accessTokenSecret}`)
res.end();
But when I tried calling the API call from the frontend VueJS, the API call doesn't get called. It triggers only thorugh the redirect URL shown in the above code. How should the API call be used from the frontend, should there be any configuration changes done for the new server (Authorization server) if added. I am newbie to this domain, if there is any lack of understanding in my problem, please let me know, I will try my best to clarify it.
Upvotes: 1
Views: 597
Reputation: 2271
Architecturally speaking, you should have:
The front-end is the OAuth client.
So, your VueJS client should:
Both APIs should validate the token presented to them to ensure that they came from the OAuth server and are trustworthy.
Here's an example (sans-OAuth server and sans-API) that shows how the client/front-end will work. It's derived from this example that has more info.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Public Client Sample</title>
</head>
<body>
<h1>Public Client Sample</h1>
<button id="startButton">Start OAuth Flow</button>
<span id="result"></span>
<div id="app">
<button v-on:click="callApi1">Call API 1</button>
<button v-on:click="callApi2">Call API 2</button>
{{ info }}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
const authorizeEndpoint = "https://localhost:8443/dev/oauth/authorize";
const tokenEndpoint = "https://localhost:8443/dev/oauth/token";
const clientId = "public_client";
var access_token;
if (window.location.search && window.location.search.length > 0) {
var args = new URLSearchParams(window.location.search);
var code = args.get("code");
if (code) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var response = xhr.response;
var message;
if (xhr.status == 200) {
var access_token = response.access_token;
axios.defaults.headers.common["Authorization"] = "Bearer " + access_token;
message = "Access Token: " + access_token;
}
else {
message = "Error: " + response.error_description + " (" + response.error + ")";
}
document.getElementById("result").innerHTML = message;
};
xhr.responseType = 'json';
xhr.open("POST", tokenEndpoint, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(new URLSearchParams({
client_id: clientId,
code_verifier: window.sessionStorage.getItem("code_verifier"),
grant_type: "authorization_code",
redirect_uri: location.href.replace(location.search, ''),
code: code
}));
}
}
document.getElementById("startButton").onclick = function() {
var codeVerifier = generateRandomString(64);
generateCodeChallenge(codeVerifier).then(function(codeChallenge) {
window.sessionStorage.setItem("code_verifier", codeVerifier);
var redirectUri = window.location.href.split('?')[0];
var args = new URLSearchParams({
response_type: "code",
client_id: clientId,
code_challenge_method: "S256",
code_challenge: codeChallenge,
redirect_uri: redirectUri
});
window.location = authorizeEndpoint + "/?" + args;
});
}
async function generateCodeChallenge(codeVerifier) {
var digest = await crypto.subtle.digest("SHA-256",
new TextEncoder().encode(codeVerifier));
return btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
}
function generateRandomString(length) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
var vueApp = new Vue({
el: '#app',
data () {
return {
info: null
}
},
methods: {
callApi1: function(event) {
console.log("Call API 1");
axios
.get('https://httpbin.org/bearer')
.then(response => (this.info = response));
},
callApi2: function(event) {
console.log("Call API 2");
axios
.get('https://httpbin.org/headers')
.then(response => (this.info = response));
}
}
});
</script>
</body>
</html>
Upvotes: 2