Auth0 Server Side Authentication with SvelteKit - javascript

I know there is no official Auth0 SvelteKit plugin for direct use, so I am trying to implement a manual approach using basic API functions in JS for server side authentication and session management, using auth0.js version 9.11.
Using the following page, I can redirect the user to the Auth0 universal login page, and the user is redirected to my landing page after a successful login. My plan is to acquire the user details from the user token on the page then send these details to a server endpoint. In the endpoint I will save these details to a database (with the access token) for login session management, then send a HttpOnly cookie for auth guards..etc.
However, I keep getting the following error within the landing page:
Uncaught (in promise) TypeError: can't assign to property "__enableIdPInitiatedLogin" on "#access_token=eyJhbGciOi...<very long token>...BNW1uzA&scope=openid%20profile%20email&expires_in=7200&token_type=Bearer&state=rtre4": not an object
The code that is used to fetch user details and doesn't work is as follows:
<script lang="ts">
import { onMount } from 'svelte';
onMount(async () => {
// Initialize the Auth0 application
var webAuth = new auth0.WebAuth({
domain: '<domain>',
clientID: '<clientId>'
});
//test
console.log(window.location.hash);
// Parse the URL and extract the Access Token
webAuth.parseHash(window.location.hash, function (err, authResult) {
if (err) {
return console.log(err);
}
// webAuth.client.userInfo(authResult.accessToken, function (err, user) {
// // This method will make a request to the /userinfo endpoint
// // and return the user object, which contains the user's information,
// // similar to the response below.
// });
});
});
</script>
<svelte:head>
<script src="https://cdn.auth0.com/js/auth0/9.11/auth0.min.js"></script>
</svelte:head>
<div class="content">
<p>Redirecting, please wait...</p>
</div>
The code that redirects the user to the universal login page is as follows:
<script lang="ts">
import Button from '#smui/button';
const login = () => {
// Initialize app - auth0 is defined as external script in <svelte:head>
var webAuth = new auth0.WebAuth({
domain: '<domain>,
clientID: '<clientid>'
});
// Calculate URL to redirect to
var url = webAuth.client.buildAuthorizeUrl({
clientID: '<clientid>', // string
responseType: 'token id_token', // code or token
redirectUri: 'http://localhost:3000/login',
scope: 'openid profile email',
state: '<state>',
nonce: '<nonce>'
});
window.location = url;
}
</script>
<svelte:head>
<script src="https://cdn.auth0.com/js/auth0/9.11/auth0.min.js"></script>
</svelte:head>
<div class="content">
<Button on:click={login}>Login</Button>
</div>
EDIT I have added the id_token to the login url construction (which also requires nonce parameter) based on Kalana's comment and modified the error screenshot based on the new code.

After some more digging, I have realized that the options supplied to the parseHash function needs to be a JSON object. You also need to have id_token in the creation of login request (thanks to the comment by Kalana), and a nonce parameter as well. You have to give the same state and nonce values to the parseHash function, too. Additionally, the hash for this function is optional; if none is given it can get it from windows.location.hash automatically.
In the end, the working code for fetching the user information from Auth0 service is as follows:
<script lang="ts">
import { onMount } from 'svelte';
onMount(async () => {
// Initialize the Auth0 application
var webAuth = new auth0.WebAuth({
domain: '<domain>',
clientID: '<clientid>'
});
// Parse the URL and extract the Access Token
webAuth.parseHash({state: "<sameState>", nonce: "<sameNonce>"}, function (err, authResult) {
if (err) {
return console.log(err);
}
webAuth.client.userInfo(authResult.accessToken, function (innerErr, user) {
// This method will make a request to the /userinfo endpoint
// and return the user object, which contains the user's information,
// similar to the response below.
if (innerErr) {
return console.log(innerErr);
}
console.log(user);
});
});
});
</script>
<svelte:head>
<script src="https://cdn.auth0.com/js/auth0/9.11/auth0.min.js"></script>
</svelte:head>
<div class="content">
<p>Redirecting, please wait...</p>
</div>

Related

How do you integrate HTML page with JWT Node JS Auth?

I have created a node.js application and have successfully define a protected route with token. I can run it via POSTMAN. I am trying to understand how then do I integrate this into a HTML page and login and call out the protected route to display on a HTML page instead of using POSTMAN. I am creating a html website and want a user login and then they can access certain link in the page.
My app.js
app.post('/api/posts', verifyToken, (req, res) => {
jwt.verify(req.token, secretKey, (err, authData) => {
if(err) {
res.sendStatus(403);
} else {
res.json({
message: 'Post created...',
authData : authData,
});
}
});
});
app.post('/api/login', (req, res) => {
// Mock user
var email=req.body.email;
var password=req.body.password;
const userdata = {
email: email,
password: password
}
console.log(userdata);
jwt.sign({userdata}, secretKey, (err, token) => {
res.json({
token
});
});
});
my HTML
<html>
<head>
</head>
<body>
<div>
<h1>Portal API Test site</h1>
<form action="/api/posts", method="POST">
<h2>Getting from Header</h2>
<input type="submit" value="Submit", >
</form>
</div>
</body>
First of all, i will suggest you to use at least a small framework to make the work easier for you such as react, svelte ... Otherwise you can jQuery to do that.
To log in a user, you need to save the JWT in cookie or localStorage. That's not a very difficult task but remember to set the header of every requests on protected routes with the Bearer Token
You can follow this example: https://github.com/chaofz/jquery-jwt-auth/blob/master/index.html
localStorage.setItem('token', yourTokenresponseFromExpress);

How to pass access token and shop name to Shopify API Node new object

I am building a public shopify app and I want to add a POST route that allows a metafield to be created.
In the shopify-api-node module the following is stated:
accessToken - Required for public apps - A string representing the permanent OAuth 2.0 access token. This option is mutually exclusive with the apiKey and password options. If you are looking for a premade solution to obtain an access token, take a look at the shopify-token module."
Here is the object that needs the shopName and accessToken
const shopify = new Shopify({
shopName: 'your-shop-name',
accessToken: 'your-oauth-token'
});
In the Shopify Node / Express documentation it has you add in /shopify/callback route qwhich includes the the Oauth:
// Shopify Callback Route //
app.get('/shopify/callback', (req, res) => {
const { shop, hmac, code, state } = req.query;
/// ... skipping over code ... ///
request.post(accessTokenRequestUrl, { json: accessTokenPayload })
.then((accessTokenResponse) => {
const accessToken = accessTokenResponse.access_token;
// DONE: Use access token to make API call to 'shop' endpoint
const shopRequestUrl = 'https://' + shop + '/admin/api/2019-04/shop.json';
const shopRequestHeaders = {
'X-Shopify-Access-Token': accessToken,
};
});
/// ... skipping over code ... ///
});
Instead of using the shopify-token module can I access/should I access this information from the /shopify/callback route in the following manner (see below)? Or is there a better way to do this / can you provide examples?
Server.js
// Declare new global variables //
var accessTokenExport;
var shopExport;
// New Function //
function exportTokens(accessToken) {
accessTokenExport = accessToken;
shopExport = shop;
}
// Shopify Callback Route //
app.get('/shopify/callback', (req, res) => {
// Export variables to New Function
exportTokens(shop, accessToken);
});
// New POST route //
app.post("/api/createMetafield", function (req, res) {
const shopify = new Shopify({
shopName: shopExport,
accessToken: accessTokenExport
});
shopify.metafield.create({
key: 'warehouse',
value: 25,
value_type: 'integer',
namespace: 'inventory',
owner_resource: 'metafield',
// owner_id: 632910392
}).then(
metafield => console.log(metafield),
err => console.error(err)
);
})
This is not the right way to use store access token
Because shopify/callback url call once only when store admin install your app but access token is useful for most of the time
To use store access token for your system you can do as below
shopify/callback API call when your app installing by shop admin that time you can store this access token in database and when it require simply getting from your db and this access token is accessible for life time till store admin not uninstall your app

Node-LinkedIn module returns "Unknown Authentication Scheme"

I'm using the node-linkedin npm package to authenticate and read information about from other users (name, job title, company name, profile pic, shared connections). I can correctly receive and store the access token (verified in my own LinkedIn profile's approved apps & console logging the token), but I am unable to return any of the requested information. My calls are copied & pasted from the package docs, but it returns the following:
2018-02-28T03:46:53.459839+00:00 app[web.1]: { errorCode: 0,
2018-02-28T03:46:53.459843+00:00 app[web.1]: message: 'Unknown authentication scheme',
2018-02-28T03:46:53.459845+00:00 app[web.1]: requestId: '3B55EVY7XQ',
2018-02-28T03:46:53.459847+00:00 app[web.1]: status: 401,
2018-02-28T03:46:53.459848+00:00 app[web.1]: timestamp: 1519789613443 }
I have included my routes below. Solely for the purpose of testing, myToken and linkedin are server-side global variables to the linkedin-controller scope. (I understand this will need to change for the final product, which is a student project.)
app.get('/companies', function (req, res) {
console.log(linkedin.connections.config.accessToken);
linkedin.companies_search.name('facebook', 1, function(err, company) {
console.log('Merpy merpy mc merpers'
,company);
// name = company.companies.values[0].name;
// desc = company.companies.values[0].description;
// industry = company.companies.values[0].industries.values[0].name;
// city = company.companies.values[0].locations.values[0].address.city;
// websiteUrl = company.companies.values[0].websiteUrl;
res.redirect("/");
});
});
app.get('/companies2', function (req, res) {
linkedin.companies.company('162479', function(err, company) {
console.log(company);
res.redirect("/");
});
});
app.get('/connections', function (req, res) {
linkedin.connections.retrieve(function(err, connections) {
console.log(connections);
res.redirect("/");
});
});
This is my authorization code, which appears to work:
app.get('/auth', function (req, res) {
// This is the redirect URI which linkedin will call to and provide state and code to verify
/**
*
* Attached to the redirect_uri will be two important URL arguments that you need to read from the request:
code — The OAuth 2.0 authorization code.
state — A value used to test for possible CSRF attacks.
*/
//TODO: validate state here to secure against CSRF
var error = req.query.error;
var error_description = req.query.error_description;
var state = req.query.state;
var code = req.query.code;
if (error) {
next(new Error(error));
}
/**
*
* The code is a value that you will exchange with LinkedIn for an actual OAuth 2.0 access
* token in the next step of the authentcation process. For security reasons, the authorization code
* has a very short lifespan and must be used within moments of receiving it - before it expires and
* you need to repeat all of the previous steps to request another.
*/
//once the code is received handshake back with linkedin to send over the secret key
handshake(req.query.code, res);
});
function handshake(code, ores) {
//set all required post parameters
var data = querystring.stringify({
grant_type: "authorization_code",
code: code,
redirect_uri: OauthParams.redirect_uri,//should match as in Linkedin application setup
client_id: OauthParams.client_id,
client_secret: OauthParams.client_secret// the secret
});
var options = {
host: 'www.linkedin.com',
path: '/oauth/v2/accessToken',
protocol: 'https:',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(data)
}
};
var req = http.request(options, function (res) {
var data = '';
res.setEncoding('utf8');
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
//once the access token is received store it
myToken = JSON.parse(data);
linkedin = Linkedin.init(myToken);
ores.redirect("/");
});
req.on('error', function (e) {
console.log("problem with request: " + e.message);
});
});
req.write(data);
req.end();
}
In my troubleshooting research, it seems I need to pass the token into the request; however, I can't find anywhere or any way to do so in the package. And with as many daily downloads as the package has, I can't possibly be the only one to experience this error. The author's Issues section of GitHub were unhelpful, as were other searches for this package's error.
My deployment: https://linkedin-api-test.herokuapp.com/
(When visiting the deployment, you must click the blue "Want to
connect to LinkedIn?" link prior to manually changing the uri
according to the routes. The results will also only display in the
Heroku logs, which is most likely largely unhelpful to you. It was
supposed to be a simple test, so I simply stole the front end from my
prior project.)
My Repo: https://github.com/SteveSonoa/LinkedIn-Test
node-linkedin Docs: https://github.com/ArkeologeN/node-linkedin/blob/master/README.md
This is my first question I haven't been able to find the answer to; I apologize if I left out anything important while asking. Thank you in advance for any help!
The solution was to pass the following token code into the linkedin variable instead of simply passing myToken:
linkedin = Linkedin.init(myToken.access_token || myToken.accessToken);
I don't understand the downvote, as no comments were left; I apologize if I left out important or generally expected information, as this was the first question I've asked. I want to make sure the solution is posted for anyone coming after me with the same issue. This issue is now solved.

How do I store and send authorization tokens with API requests using OAuth?

I'm trying to set my application up so I can use the Spotify API. Their API requires an Authorization token along with every request, and this token is different every user session. I have successfully implemented OAuth2 login using the 'passport-spotify' module (detail below) and have the token, which I am currently storing in my database. Once it is in the database, it is also available in my Redux store.
Functioning Passport strategy:
const spotifyConfig = {
clientID: process.env.SPOTIFY_CLIENT_ID,
clientSecret: process.env.SPOTIFY_CLIENT_SECRET,
callbackURL: process.env.SPOTIFY_CALLBACK
}
const strategy = new SpotifyStrategy(spotifyConfig, (accessToken, refreshToken, profile, done) => {
const spotifyId = profile.id
const name = profile.displayName
const email = profile.emails[0].value
User.find({where: {spotifyId}})
.then(foundUser => (foundUser
? foundUser.update({accessToken, refreshToken}).then(() => done(null, foundUser))
: User.create({name, email, spotifyId, accessToken, refreshToken})
.then(createdUser => done(null, createdUser))
))
.catch(done)
})
passport.use(strategy)
router.get('/', passport.authenticate('spotify', {scope: ['user-read-email'], showDialog: true}))
router.get('/callback', passport.authenticate('spotify', {
successRedirect: '/home',
failureRedirect: '/login'
}))
What I am currently stuck on is how to set up my API requests so it accesses that token on every call. The 'spotify-web-api-node' node module has a setCredentials method, but I can't figure out how to access the token.
Semi-functioning API call (it makes the API request but gives me a 403 unauthorized):
const SpotifyWebApi = require('spotify-web-api-node');
const spotifyApi = new SpotifyWebApi();
spotifyApi.setCredentials({
clientId: 'my client id',
clientSecret: 'my client secret',
redirectUri: 'http://localhost:8888/auth/spotify/callback',
refreshToken: 'cant figure out how to properly include this',
accessToken: 'and this.',
});
export function searchMetallica(){
return spotifyApi.searchArtists('Metallica')
.then(function(data) {
console.log(data.body);
}, function(err) {
console.error(err);
});
}
I hope this isn't too much a of a newbie question. Thanks in advance.
You're very close!
You need to pass the token to the setAccessToken() method on the Spotify wrapper, in your case:
spotifyApi.setAccessToken(<youraccesstoken>);
You can set the refresh token in a predictably similar way:
spotifyApi.setRefreshToken(<yourrefreshtoken>);
Easy peasy!
But, there's a catch. If you use this spotifyApi for all your calls, this will set the same access token for all those calls! You need to make sure to use the appropriate access token for each user, so user A can't do actions for user B and vice versa.
You can get around this by simply instantiating the API wrapper and setting the access token at the point when the user logs in, or at the point when the call is made. For example, a call to get top tracks may look like (using Express for convenience):
app.get('/myendpoint', function (request, response) {
const loggedInSpotifyApi = new SpotifyWebApi();
loggedInSpotifyApi.setAccessToken(request.access_token);
// Get top tracks!
loggedInSpotifyApi.getMyTopTracks()
.then(function(data) {
response.send(data.body);
}, function(err) {
console.error(err);
});
});
Here's a full working Glitch that shows the Authorization Code flow and spotify-web-api-node: https://glitch.com/edit/#!/spotify-authorization-code
Let me know if you have any questions!

How to request IdentityServer3 client access token from JavaScript?

I have an authentication service built on IdentityServer3 and a WebAPI Shopper service that is secured with IdentityServer Bearer Token Authentication. I've built a simple MVC client app that can access the Shopper service either as a client app with an access token or on behalf of an authenticated user with an identity token. The Shopper service will return more data to the authenticated user.
Now I'm trying to build a JavaScript client that does the same two-tier level of access to the Shopper service. So far, following some IdentityServer3 JavaScript client examples, I've got the JS client successfully calling the Shopper service on behalf of an authenticated user. (I will probably need to reorganize the code a bit to accommodate the non-authenticated scenario, but that shouldn't be too difficult.) What I don't know how to do from the JavaScript code is request the client access token from the Authentication service, i.e. the JavaScript equivalent of the server-side TokenClient.RequestClientCredentialsAsync("shopper-service") in the MVC client. Does anyone know how to request that token from JavaScript or know of a sample that shows how to do it? Here's the JavaScript code that I have so far for the authenticated case and below that is the working MVC client code:
function display(selector, data) {
if (data && typeof data === 'string') {
data = JSON.parse(data);
}
if (data) {
data = JSON.stringify(data, null, 2);
}
$(selector).text(data);
}
var settings = {
authority: 'https://localhost:44332',
client_id: 'js-sample',
popup_redirect_uri: 'http://localhost:15264/popup.html',
response_type: 'id_token token',
scope: 'openid orvis-shopper-service',
filterProtocolClaims: false
};
var manager = new Oidc.UserManager(settings);
var user;
manager.events.addUserLoaded(function (loadedUser) {
user = loadedUser;
display('.js-user', user);
});
$('.js-login').on('click', function () {
manager
.signinPopup()
.catch(function (error) {
console.error('error while logging in through the popup', error);
});
});
$('.js-call-api').on('click', function () {
var headers = {};
if (user && user.access_token) {
headers['Authorization'] = 'Bearer ' + user.access_token;
}
$.ajax({
url: 'https://localhost:44368/api/Shopper/{5FA13934-AD20-4AB2-A386-11653D71AE55}',
method: 'GET',
dataType: 'json',
headers: headers
}).then(function (data) {
display('.js-api-result', data);
}).catch(function (error) {
display('.js-api-result', {
status: error.status,
statusText: error.statusText,
response: error.responseJSON
});
});
});
The client app code works as I intend and looks like this:
public async Task<ActionResult> Index()
{
string tokenValue;
var user = User as ClaimsPrincipal;
if (user.Identity.IsAuthenticated)
{
tokenValue = user.FindFirst("access_token").Value;
}
else
{
var tokenResponse = await GetTokenAsync();
tokenValue = tokenResponse.AccessToken;
}
var result = await CallShopperService(tokenValue);
ViewBag.Json = result;
return View();
}
private async Task<TokenResponse> GetTokenAsync()
{
var client = new TokenClient(
"https://localhost:44332/connect/token",
"mvc-sample-svc",
"mvcsamplesecret");
return await client.RequestClientCredentialsAsync("shopper-service");
}
private async Task<string> CallShopperService(string token)
{
var client = new HttpClient();
client.SetBearerToken(token);
var json = await client.GetStringAsync("https://localhost:44368/api/Shopper/{5FA13934-AD20-4AB2-A386-11653D71AE55}");
return json;
}
JS implicit client is also able to get back you access token, as long as you set token in response_type which you did according to your pasted script.
The Oidc-client.js builds up an authorize challenge URL and redirect browser to it, which is where login flow begins. After user signs in, then they get bounced back to your client page following same redirect path. When your client page loaded (depends on which flow your client is configured, it should be hash fragment by default), oidc-client grabs token from URL (everything after #) and transforms it into JSON object then save in local storage (which means you can check it there too).
I would suggest following method to help you debug:
Turn on traffic monitoring tool (e.g. fiddler) to ensure response that returned back from your identity server after login does contains access token (you can decode token string using https://jwt.io/), if not, check if authorize request url formed correctly
If response returned from identity server does contains access token , then you can debug oidc-client.js javascript by setting a breakpoint at _signinEnd method
Hope it helps
Updated, from comment section
For "anonymous token" senario? See this example if that is what you are looking for github.com/IdentityServer/IdentityServer3/issues/1953

Categories

Resources