I'm new to Koa.js, and I'm trying to create a simple authentication flow with Spotify. Here's my code:
import koa from "koa";
import querystring from "node:querystring";
const port = 54832;
const client_id = "myClientID";
const redirect_uri = `http://localhost:${port}/callback`;
const scope = "user-read-currently-playing";
export default function requestAuth() {
const server = new koa();
let response = {};
server.use(async (ctx) => {
const { url, method } = ctx.request;
if (method === "GET" && url === "/login") {
ctx.body = "You will be redirected to Spotify auth page";
ctx.redirect(
"https://accounts.spotify.com/authorize?" +
querystring.stringify({
response_type: "code",
client_id: client_id,
scope: scope,
redirect_uri: redirect_uri,
})
);
} else if (method === "GET" && url.includes("/callback")) {
if (!ctx.request.query.error) {
response.code = ctx.request.query.code;
} else {
console.log(ctx.request.query.error);
}
}
});
server.listen(port);
return response;
}
I want to redirect users to the Spotify login page when they visit the /login
URL. However, instead of redirecting to Spotify, I am being redirected to the local URL http://localhost:54832/response_type=code&client_id=myClientID&redirect_uri=http%3A%2F%2Flocalhost%3A54832&scope=user-read-currently-playing
and getting a "Not Found" output in the response body.
Am I doing something wrong with the way I'm handling the redirect? Could it be an issue with my server setup or with the way Koa.js handles redirects? Any suggestions on how to troubleshoot or fix this?
A little earlier I had the same problem with Express JS. As I understood then, the problem was caching, which is difficult to disable in this framework. This is exactly why I switched to Koa JS. But since the problem persists in this case too, I am completely confused
The function requestAuth()
returns an object immediately, before any HTTP requests are processed, so it's always empty and not useful.
Here's how you might refactor the code to make it work more effectively, adhering to the typical use of Koa for handling multiple asynchronous requests
Save as demo.js
import Koa from 'koa'
import Router from 'koa-router'
import bodyParser from 'koa-bodyparser'
import cors from '@koa/cors'
import axios from 'axios'
import querystring from "querystring";
const app = new Koa()
const router = new Router()
const CLIENT_ID = "{your client id}";
const CLIENT_SECRET = "{your client secret}";
const PORT = 3000; // replace your port
const REDIRECT_URI = `http://localhost:${PORT}/callback`
const SCOPE = 'user-read-currently-playing'
app.use(cors());
app.use(bodyParser());
const getToken = async (code) => {
try {
const response = await axios.post('https://accounts.spotify.com/api/token',
querystring.stringify({
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': REDIRECT_URI
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
auth: {
username: CLIENT_ID,
password: CLIENT_SECRET
}
});
return response.data.access_token
} catch (err) {
console.error(err)
throw err
}
}
router.get('/login', async (ctx) => {
const redirect_url = `https://accounts.spotify.com/authorize?${
querystring.stringify({
response_type: 'code',
client_id: CLIENT_ID,
scope: SCOPE,
state: '123456',
redirect_uri: REDIRECT_URI,
prompt: 'consent'
})}`
await ctx.redirect(redirect_url)
})
router.get('/callback', async (ctx) => {
const code = ctx.query.code;
try {
const access_token = await getToken(code);
ctx.body = { 'access_token': access_token };
} catch (error) {
console.error(error.message)
ctx.status = 500
ctx.body = { error: 'Failed to retrieve access token' }
}
})
app.use(router.routes()).use(router.allowedMethods())
app.listen(PORT, () => {
console.log(`Listening on http://localhost:${PORT}`)
})
npm install koa koa-router koa-bodyparser @koa/cors axios querystring
package.json
{
"type": "module",
"dependencies": {
"@koa/cors": "^5.0.0",
"axios": "^1.6.8",
"cors": "^2.8.5",
"koa": "^2.15.3",
"koa-bodyparser": "^4.4.1",
"koa-router": "^12.0.1",
"querystring": "^0.2.1"
}
}
node demo.js
http://localhost:3000/login
Replace 3000 into you port number