这是我正在网络上处理的一个博览会项目。它尝试使用 Spotify Web API 进行身份验证。访问令牌从 Spotify 身份验证页面返回,可在 req.user.accessToken 的回调中使用。
但是,在随后调用的其他路由中,req.user.accessToken 和 req.session.accessToken 都是未定义的。尽管在回调中尝试将令牌分配给会话,但仍然如此。
这里,访问令牌在身份验证回调中可用...
app.get(
"/auth/spotify/callback",
passport.authenticate("spotify", { failureRedirect: "/" }),
(req, res) => {
req.session.accessToken = req.user.accessToken;
console.log("callback: " + req.session.accessToken);
res.redirect("http://localhost:8081");
}
);
但其他路线没有
app.get("/spotify/get-currently-playing", async (req, res) => {
try {
const accessToken = req.session && req.session.accessToken;
if (!accessToken) {
throw new Error("Access token not found");
}
const userData = await getCurrentSongInfo(accessToken);
res.json(userData);
...
中间件之前已设置如下
app.use(
session({
secret: "x",
resave: false,
saveUninitialized: false,
})
);
app.use(passport.initialize());
app.use(passport.session());
passport.use(
new SpotifyStrategy(
{
clientID: "x",
clientSecret: "x",
callbackURL: "http://localhost:5001/auth/spotify/callback",
},
(accessToken, refreshToken, expires_in, profile, done) => {
// Store the access token in the session
if (accessToken) {
profile.accessToken = accessToken;
console.log(profile);
done(null, profile);
} else {
done(new Error("Failed to retrieve access token"));
}
}
)
);
您需要从用户会话数据中获取
来自
app.get(
"/auth/spotify/callback",
passport.authenticate("spotify", { failureRedirect: "/" }),
(req, res) => {
req.session.accessToken = req.user.accessToken;
console.log("callback: " + req.session.accessToken);
res.redirect("http://localhost:8081");
}
);
致
app.get(
"/auth/spotify/callback",
passport.authenticate("spotify", { failureRedirect: "/" }),
(req, res) => {
if (req.user && req.user.accessToken) {
console.log("callback: " + req.user.accessToken);
} else {
console.log('callback: accessToken is none');
}
res.redirect("http://localhost:8081");
}
);
package.json
{
"dependencies": {
"consolidate": "^1.0.3",
"cors": "^2.8.5",
"express": "^4.19.1",
"express-session": "^1.18.0",
"nunjucks": "^3.2.4",
"passport": "^0.7.0",
"passport-spotify": "^2.0.0"
}
}
文件树
client_id
、client_secret
和 redirect_uri
通过浏览器打开网址
https://developer.spotify.com/dashboard
通过浏览器打开网址
https://open.spotify.com/
使用您的客户端 ID/密码和重定向 URI 另存为
server.js
。
const express = require('express');
const cors = require('cors');
const session = require('express-session');
const passport = require('passport');
const SpotifyStrategy = require('passport-spotify').Strategy;
const consolidate = require('consolidate');
const CLIENT_ID = '<your client id>';
const CLIENT_SECRET = '<your client secret>';
const port = 5001; // make sure your REDIRECT_URI's port
const authCallbackPath = '/auth/spotify/callback';
const REDIRECT_URI = `http://localhost:${port}${authCallbackPath}`;
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (obj, done) {
done(null, obj);
});
passport.use(
new SpotifyStrategy(
{
clientID: CLIENT_ID,
clientSecret: CLIENT_SECRET,
callbackURL: REDIRECT_URI,
},
function (accessToken, refreshToken, expires_in, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
try {
profile.accessToken = accessToken;
console.log(`accessToken is ${profile.accessToken}`);
return done(null, profile);
} catch (error) {
return done(error);
}
});
}
)
);
const app = express();
app.use(cors()); // Enable CORS
app.use(
session({ secret: 'keyboard cat', resave: false, saveUninitialized: false })
);
// Initialize Passport! Also use passport.session() middleware, to support
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(__dirname + '/public'));
app.engine('html', consolidate.nunjucks);
app.get('/', function (req, res) {
res.render('index.html', { user: req.user });
});
app.get('/account', ensureAuthenticated, function (req, res) {
if (req.user && req.user.accessToken) {
console.log(`In account accessToken is ${req.user.accessToken}`);
} else {
console.log('In account accessToken is none');
}
res.render('account.html', { user: req.user });
});
app.get('/login', function (req, res) {
res.render('login.html', { user: req.user });
});
app.get('/logout', function (req, res) {
req.logout();
res.redirect('/');
});
// GET /auth/spotify
app.get(
'/auth/spotify',
passport.authenticate('spotify', {
scope: ['user-read-email', 'user-read-private'], // you can add more scopes it depends on your require APIs call
showDialog: false, // if set true force shows login UI
})
);
// GET /callback
app.get(
authCallbackPath,
passport.authenticate('spotify', { failureRedirect: '/login' }),
function (req, res) {
res.redirect('/');
}
);
// Logout route. it has a defect, I think `passport-spotify` not address it.
app.get('/logout', function (req, res) {
req.logout(); // No need for a callback function here
res.redirect('/'); // Redirect to login page or any desired page
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
}
HTML 文件
account.html
{% extends 'layout.html' %} {% block content %}
<p>ID: {{ user.id }}</p>
<p>Token: {{ user.accessToken }}</p>
<p>Profile:
<a href=" {{ user.profileUrl }}">{{ user.profileUrl }}</a>
</p>
{% endblock %}
index.html
{% extends 'layout.html' %}
{% block content %}
{% if not user %}
<h2>Welcome! Please log in.</h2>
<p><a href="/login">login</a></p>
{% else %}
<h2>Hello, {{ user.username }}.</h2>
<p>Your email is {{ user.emails[0].value }}</p>
{% endif %}
{% endblock %}
layout.html
<!DOCTYPE html>
<html>
<head>
<title>Passport-Spotify Example</title>
</head>
<body>
{% if not user %}
<p>
<a href="/">Home</a> |
<a href="/login">Log In</a>
</p>
{% else %}
<p>
<a href="/">Home</a> |
<a href="/account">Account</a> |
<a href="/logout">Log Out</a>
</p>
{% endif %} {% block content %}{% endblock %}
</body>
</html>
login.html
{% extends 'layout.html' %} {% block content %}
<a href="/auth/spotify">Login with Spotify</a>
{% endblock %}
结果