Unable to verifyIdToken with firebase.auth() - firebase

Using OAuth with Github on my client, I send a fetch request with the resulting token to my server. I'm getting the token as expected, but am unable to execute firebase.auth().verifyIdToken to get the token. My SDK is authenticated with a certificate credential following the the admin SDK setup.
My clientAuth middleware:
const firebase = require('firebase-admin');
const db = require('../db');
module.exports = async (req, res, next) => {
try {
const tokenId = req.get('Authorization').split('Bearer ')[1];
console.log(tokenId) //yay, token
const validToken = await firebase.auth().verifyIdToken(tokenId);
console.log(validToken.uid) //error
return (validToken && validTeam) ? next() : res.status(401).end();
} catch (e) {
res.status(401).end();
}
};
The error I get is 'Decoding Firebase ID token failed. Make sure you passed the entire string JWT which represents an ID token.' How do I go about verifying/ decoding this token?

I think this could be good, please copy this function using by google
const admin = require('firebase-admin')
exports.validateFirebaseIdToken = async (req, res, next) => {
console.log('Check if request is authorized with Firebase ID token');
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!(req.cookies && req.cookies.__session)) {
console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
'Make sure you authorize your request by providing the following HTTP header:',
'Authorization: Bearer <Firebase ID Token>',
'or by passing a "__session" cookie.');
res.status(403).send('Unauthorized');
return;
}
let idToken;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1];
} else if (req.cookies) {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
res.status(403).send('Unauthorized');
return;
}
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
//console.log('ID Token correctly decoded', decodedIdToken);
req.user = decodedIdToken;
next();
return;
} catch (error) {
console.error('Error while verifying Firebase ID token:', error);
res.status(403).send('Unauthorized');
return;
}
};

Related

Firebase google authentication in Chrome Extension - Error getting credential

I'm working on updating a chrome extension to MV3, and therefore I can't use the firebase UI to login any more. What I trying to do is use chrome.identity.launchWebAuthFlow, get the token, create the credential, and sign in with firebase.
Here's what I have:
function launchGoogleAuthFlow(interactive) {
return new Promise((resolve, reject) => {
console.log('launching webauthflow')
const manifest = chrome.runtime.getManifest();
const clientId = encodeURIComponent(manifest.oauth2.client_id);
const scopes = encodeURIComponent(manifest.oauth2.scopes.join(' '));
let redirectUri = chrome.identity.getRedirectURL();
let nonce = Math.random().toString(36).substring(2, 15)
const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
authUrl.searchParams.set('client_id', clientId);
authUrl.searchParams.set('response_type', 'id_token');
authUrl.searchParams.set('redirect_uri', redirectUri);
// Add the OpenID scope. Scopes allow you to access the user’s information.
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('nonce', nonce);
// Show the consent screen after login.
authUrl.searchParams.set('prompt', 'consent');
chrome.identity.launchWebAuthFlow(
{
'url': authUrl.href,
'interactive': interactive
},
(redirectedTo) => {
if (chrome.runtime.lastError) {
console.log(chrome.runtime.lastError.message);
resolve(null)
}
else {
let idToken = redirectedTo.substring(redirectedTo.indexOf('id_token=') + 9)
idToken = idToken.substring(0, idToken.indexOf('&'))
resolve(idToken)
}
}
)
})
}
launchGoogleAuthFlow(true).then((token)=>{
if (token) {
console.log('token:' + token);
const credential = GoogleAuthProvider.credential(null, token);
console.log(credential);
signInWithCredential(auth, credential).then((result) => {
showMain();
document.getElementById('loggedInAs').textContent = result.email;
console.log("Success!!!")
console.log(result)
}).catch((error) => {
// You can handle errors here
console.log(error)
});
} else {
console.error('The OAuth token was null');
}
});
console.log('finished authflow');
}
I'm getting prompted to sign in with my google credentials, then in the console I get Failed to load resource: the server responded with a status of 400 ()
and then the a log from SignInWithCredentials
FirebaseError: Firebase: Unsuccessful check authorization response from Google: {
"error_description": "Invalid Value"
}
(auth/invalid-credential).
at _errorWithCustomMessage (index-6bd8d405.js:453:1)
at _performFetchWithErrorHandling (index-6bd8d405.js:973:1)
at async _performSignInRequest (index-6bd8d405.js:988:1)
at async _signInWithCredential (index-6bd8d405.js:4721:1)
In the response_type you are requesting an id_token:
authUrl.searchParams.set('response_type', 'id_token');
So your launchGoogleAuthFlow returns an id_token and that's it what you must give to GoogleAuthProvider.credential. This method expects an a id_token as the first parameter and an a access_token as the second parameter.
So all you have to do is change from:
const credential = GoogleAuthProvider.credential(null, token);
to:
const credential = GoogleAuthProvider.credential(token);
Everything should works fine.
If you may want the access_token you must request response_type=token and remove nonce. Finally you'll need to extract the returned access_token from response URL (your redirectedTo variable) as you did with id_token.
PS: In your code I also noticed that you got scopes from manifest but did not use them while requesting the token.

Google OAuth Refresh Tokens not returning Valid Access Tokens

I have a Firebase application that authenticates a user and returns an access token that I can then use to access the Google Calendar and Sheets API. I also save the refreshToken. Sample code for authenticated token:
firebase
.signInWithGoogle()
.then(async (socialAuthUser) => {
let accessToken = socialAuthUser.credential.accessToken // token to access Google Sheets API
let refreshToken = socialAuthUser.user.refreshToken
this.setState({accessToken, refreshToken})
})
After 1 hour, the accessToken expires. Firebase auth provides a refresh token on the user object after sign-in
I use that refresh token to re-authenticate and get a new access_token by posting to:
https://securetoken.googleapis.com/v1/token?key=firebaseAppAPIKey
That new access token does not work for Google APIs anymore, and it doesn't have the authorized scopes anymore. I also try sending it to
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token="refreshToken"
It gives me the error "Invalid token". When I use the original token from firebase, it works just fine.
Anyone else encountering a similar issue? I haven't figured out a way to refresh the original access token with the correct access scopes without making the user sign-out and sign-in again.
Thanks!
I was finally able to solve it after many attempts.
Posted detailed solution on Medium: https://inaguirre.medium.com/reusing-access-tokens-in-firebase-with-react-and-node-3fde1d48cbd3
On the client, I used React with the Firebase library, and on the server I used Node.js with the packages google-apis and the firebase-admin skd package linked to the same Firebase project.
Steps:
(CLIENT) Send a request to the server to generate an authentication link
(SERVER) Generate Auth Link and send it back to the client using the getAuthLink() from googleapis. Sign in with Google and handle the redirect.
(SERVER) On the redirect route, use the code from Google on the query string to authenticate the user and get his user credentials. Use these credentials to check if the user is registered on Firebase.
(SERVER) If the user is registered, get the access and refresh tokens using the oauth2.getTokens(code), update refresh token on the user profile in the database. If the user is not registered, create a new user with firebase.createUser(), also create the user profile on the database with the refresh token.
(SERVER) Use firebase.createCustomToken(userId) to send an id_token back to client and authenticate.
(SERVER) Use a res.redirect({access_token, referesh_token, id_token}) to send credentials back to client.
(CLIENT) On the client, use the signInWithCustomToken(id_token) to authenticate, also restructure the query to obtain access_token and refresh_token to send API calls.
(CLIENT) Set an expiration date for the access token. On each request, check if the current date is higher than the expiration date. If it is, request a new token to https://www.googleapis.com/oauth2/v4/token with the refresh token. Otherwise use the access_token stored.
Most stuff happens when handling the Google Redirect after authentication. Here's an example of handling auth and tokens on the backend:
const router = require("express").Router();
const { google } = require("googleapis");
const { initializeApp, cert } = require("firebase-admin/app");
const { getAuth } = require("firebase-admin/auth");
const { getDatabase } = require("firebase-admin/database");
const serviceAccount = require("../google-credentials.json");
const fetch = require("node-fetch");
initializeApp({
credential: cert(serviceAccount),
databaseURL: "YOUR_DB_URL",
});
const db = getDatabase();
const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
"http://localhost:8080/handleGoogleRedirect"
);
//post to google auth api to generate auth link
router.post("/authLink", (req, res) => {
try {
// generate a url that asks permissions for Blogger and Google Calendar scopes
const scopes = [
"profile",
"email",
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/calendar",
];
const url = oauth2Client.generateAuthUrl({
access_type: "offline",
scope: scopes,
// force access
prompt: "consent",
});
res.json({ authLink: url });
} catch (error) {
res.json({ error: error.message });
}
});
router.get("/handleGoogleRedirect", async (req, res) => {
console.log("google.js 39 | handling redirect", req.query.code);
// handle user login
try {
const { tokens } = await oauth2Client.getToken(req.query.code);
oauth2Client.setCredentials(tokens);
// get google user profile info
const oauth2 = google.oauth2({
version: "v2",
auth: oauth2Client,
});
const googleUserInfo = await oauth2.userinfo.get();
console.log("google.js 72 | credentials", tokens);
const userRecord = await checkForUserRecord(googleUserInfo.data.email);
if (userRecord === "auth/user-not-found") {
const userRecord = await createNewUser(
googleUserInfo.data,
tokens.refresh_token
);
const customToken = await getAuth().createCustomToken(userRecord.uid);
res.redirect(
`http://localhost:3000/home?id_token=${customToken}&accessToken=${tokens.access_token}&userId=${userRecord.uid}`
);
} else {
const customToken = await getAuth().createCustomToken(userRecord.uid);
await addRefreshTokenToUserInDatabase(userRecord, tokens);
res.redirect(
`http://localhost:3000/home?id_token=${customToken}&accessToken=${tokens.access_token}&userId=${userRecord.uid}`
);
}
} catch (error) {
res.json({ error: error.message });
}
});
const checkForUserRecord = async (email) => {
try {
const userRecord = await getAuth().getUserByEmail(email);
console.log("google.js 35 | userRecord", userRecord.displayName);
return userRecord;
} catch (error) {
return error.code;
}
};
const createNewUser = async (googleUserInfo, refreshToken) => {
console.log(
"google.js 65 | creating new user",
googleUserInfo.email,
refreshToken
);
try {
const userRecord = await getAuth().createUser({
email: googleUserInfo.email,
displayName: googleUserInfo.name,
providerToLink: "google.com",
});
console.log("google.js 72 | user record created", userRecord.uid);
await db.ref(`users/${userRecord.uid}`).set({
email: googleUserInfo.email,
displayName: googleUserInfo.name,
provider: "google",
refresh_token: refreshToken,
});
return userRecord;
} catch (error) {
return error.code;
}
};
const addRefreshTokenToUserInDatabase = async (userRecord, tokens) => {
console.log(
"google.js 144 | adding refresh token to user in database",
userRecord.uid,
tokens
);
try {
const addRefreshTokenToUser = await db
.ref(`users/${userRecord.uid}`)
.update({
refresh_token: tokens.refresh_token,
});
console.log("google.js 55 | addRefreshTokenToUser", tokens);
return addRefreshTokenToUser;
} catch (error) {
console.log("google.js 158 | error", error);
return error.code;
}
};
router.post("/getNewAccessToken", async (req, res) => {
console.log("google.js 153 | refreshtoken", req.body.refresh_token);
// get new access token
try {
const request = await fetch("https://www.googleapis.com/oauth2/v4/token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
refresh_token: req.body.refresh_token,
grant_type: "refresh_token",
}),
});
const data = await request.json();
console.log("google.js 160 | data", data);
res.json({
token: data.access_token,
});
} catch (error) {
console.log("google.js 155 | error", error);
res.json({ error: error.message });
}
});
module.exports = router;
For anyone who comes across this now, there is a much easier way at this point.
I was able to solve this by implementing a blocking function that simply saved the refreshToken and exiry date to firestore. You can then query this from your frontend to get the tokens there as well.
Be sure to enable the refreshToken in the firebase settings, otherwise the blocking function won't have access to it.
https://firebase.google.com/docs/auth/extend-with-blocking-functions
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import {
AuthEventContext,
AuthUserRecord,
} from "firebase-functions/lib/common/providers/identity";
admin.initializeApp();
exports.beforeSignIn = functions.auth
.user()
.beforeSignIn((user: AuthUserRecord, context: AuthEventContext) => {
// If the user is created by Yahoo, save the access token and refresh token
if (context.credential?.providerId === "yahoo.com") {
const db = admin.firestore();
const uid = user.uid;
const data = {
accessToken: context.credential.accessToken,
refreshToken: context.credential.refreshToken,
tokenExpirationTime: context.credential.expirationTime,
};
// set will add or overwrite the data
db.collection("users").doc(uid).set(data);
}
});

Getting some metadata from cloud storage to cloud function

I want to grab a json file from my cloud storage to use in cloud function (this is in the '/post-account' route). Although, I'm not sure if I'm doing it correctly, as the error logs are printing that web3 cant instantiate a contract without its abi (json interface).
I just need some hardcoded metadata for the cloud function. I have tried deploying with the json file local to the function dir, and requiring in the index.js, this did not work.
I could try copying the entire json interface into index.js as var, but this is thousands of lines of metadata.
index.js
'use strict';
const Web3 = require('web3');
const Contract = require('web3-eth-contract');
const axios = require('axios')
const fs = require('fs');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const express = require('express');
const cookieParser = require('cookie-parser')();
const cors = require('cors');
const app = express();
//options for cors midddleware
const options = {
allowedHeaders: [
'Authorization',
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'X-Access-Token',
],
credentials: true,
methods: 'GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE',
origin: 'https://test-cf-97bfc.web.app',
preflightContinue: false,
};
// set headers for app.
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'https://test-cf-97bfc.web.app');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
// Express middleware that validates Firebase ID Tokens passed in the Authorization HTTP header.
// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:
// `Authorization: Bearer <Firebase ID Token>`.
// when decoded successfully, the ID Token content will be added as `req.user`.
const validateFirebaseIdToken = async (req, res, next) => {
console.log('Check if request is authorized with Firebase ID token');
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!(req.cookies && req.cookies.__session)) {
console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
'Make sure you authorize your request by providing the following HTTP header:',
'Authorization: Bearer <Firebase ID Token>',
'or by passing a "__session" cookie.');
res.status(403).send('Unauthorized');
return;
}
let idToken;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1];
} else if (req.cookies) {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
res.status(403).send('Unauthorized');
return;
}
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
console.log('ID Token correctly decoded', decodedIdToken);
req.user = decodedIdToken;
next();
return;
} catch (error) {
console.error('Error while verifying Firebase ID token:', error);
res.status(403).send('Unauthorized');
return;
}
};
app.use(cors(options))
app.use(cookieParser);
app.use(validateFirebaseIdToken);
app.get('/hello', (req, res) => {
// #ts-ignore
res.send(`Hello ${req.user.email}`);
});
app.post('/post-account', async (req, res) => {
const { account, price } = req.body;
console.log('account' + account);
console.log('price' + price);
//changing to matic node
const web3 = new Web3.providers.HttpProvider('https://ropsten.infura.io/v3/09cbbdd922284d95a70c627ddf29012d');
const address = "0xE981aFEeA8E3dB77003C1D56e7c1D42e470Ea775";
//fetch json from cloud storage. Incorrect?
const abi = axios.get('https://firebasestorage.googleapis.com/v0/b/test-cf-97bfc.appspot.com/o/abis%2Fabi.json?alt=media&token=26bb0e2f-872c-4ee9-ac0f-525b9d84af39')
console.log('after artifact call');
Contract.setProvider(web3);
//error here.
const contract = new Contract(abi, address);
await contract.methods.totalSupply().call( function(error, result){
if (error) {
console.log(error);
res.status(400).end;
}
else if (result) {
res.status(200).json({ txResolved: "NFT awarded post", account: account, currentSupply: result })
}
});
});
exports.testAPI = functions.https.onRequest(app);
Before you can fetch any file from Cloud storage check the IAM policies and test if you can access it. If the Cloud Function exists inside the same project as the Cloud Storage file there should be no problem with auth unless you modified these rules to be more strict. If you need to finetune the network access for the function you can check out these links for VPC networks and for general networks
In a Cloud Functions you can use the npm #google-cloud/storage to get a file from a cloud storage. You can get a file like show in documentation link
const {Storage} = require('#google-cloud/storage');
const storage = new Storage();
const myBucket = storage.bucket('my-bucket');
const file = myBucket.file('my-file');
file.get(function(err, file, apiResponse) {
// file.metadata` has been populated.
});
//-
// If the callback is omitted, we'll return a Promise.
//-
file.get().then(function(data) {
const file = data[0];
const apiResponse = data[1];
// Code to use json file
});
Just remember to include the dependency in your package.json
"dependencies": {
"#google-cloud/storage": "^5.8.1"
}

Unity Firebase functions.https.onRequest call error is always Internal with error message: Response is not valid JSON object

I'm trying to handle Firebase cloud function errors in Unity but I cannot always receive an error with error code "Internal" and error message "Response is not valid JSON object."
After reading through the Firebase documentation and some other stack overflow questions I understand that there is a difference between functions.https.onCall and functions.https.onRequest. We are currently using onRequest because we need to access these functions from web, in addition to android and iOS.
The problematic cloud code:
app.post("/test", (req, res) => {
res.status(500).send("This message will never appear in the editor");
});
exports.app = functions.https.onRequest(app);
exports.ThisTestWorks = functions.https.onCall((data, context) => {
throw new functions.https.HttpsError('invalid-argument', 'This message will appear in the editor');
});
Unity code:
FirebaseProcess retVal = new FirebaseProcess();
FunctionInst.GetHttpsCallable("app/test").CallAsync(request).ContinueWith(t => {
if (t.IsFaulted || t.IsCanceled) {
foreach (var inner in t.Exception.InnerExceptions) {
if (inner is FunctionsException) {
var e = (FunctionsException) inner;
Debug.Log("e.ErrorCode: " + e.ErrorCode + ", e.Message: " + e.Message);
} else {
Debug.Log("inner.Message: " + inner.Message);
}
}
Debug.Log("t.Exception.Message " + t.Exception.Message);
Debug.Log("t.Status " + t.Status);
} else {
retVal.Success = true;
retVal.ResultJSON = t.Result.Data as string;
}
retVal.IsRunning = false;
});
This code will return the following logs:
e.ErrorCode: Internal, e.Message: Response is not valid JSON object.
t.Exception.Message One or more errors occurred.
t.Status Faulted
Full (relevant) cloud code:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors')({origin: true});
const express = require('express');
const cookieParser = require('cookie-parser')();
const app = express();
admin.initializeApp();
const validateFirebaseIdToken = async (req, res, next) => {
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!(req.cookies && req.cookies.__session)) {
console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
'Make sure you authorize your request by providing the following HTTP header:',
'Authorization: Bearer <Firebase ID Token>',
'or by passing a "__session" cookie.');
res.status(403).send('Unauthorized');
return;
}
let idToken;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1];
} else if(req.cookies) {
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
res.status(403).send('Unauthorized');
return;
}
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
req.user = decodedIdToken;
next();
return;
} catch (error) {
console.error('Error while verifying Firebase ID token:', error);
res.status(403).send('Unauthorized');
return;
}
};
app.use(cors);
app.use(cookieParser);
app.use(validateFirebaseIdToken);
app.post("/test", (req, res) => {
res.status(500).send("This message will never appear in the editor");
});
exports.ThisTestFails = functions.https.onRequest(app);
exports.ThisTestWorks = functions.https.onCall((data, context) => {
throw new functions.https.HttpsError('invalid-argument', 'This message will appear in the editor');
});
Is there a reason that this doesn't return a custom error in the Unity editor? What am I missing here?
Thanks in advance!

OAuth2 fails to return auth token using simple-oauth2 and Firebase Functions for Spotify Authentication

I have been working on a oauth2 flow for spotify by following this similar tutorial by the Firebase team for Instagram HERE
I am able to submit my credentials and return the user code and state in the url, but when I run the method to submit the code to return an auth token, the auth token that I print to console in the Firebase functions returns: Auth Token Error Not Found. Here's my workflow:
Here's the Spotify docs
FIRST, I have a function to configure my spotifyOAuth:
function spotifyOAuth2Client() {
// Spotify OAuth 2 setup
const credentials = {
client: {
id: functions.config().spotify.clientid,
secret: functions.config().spotify.clientsecret,
},
auth: {
tokenHost: 'https://accounts.spotify.com',
authorizePath: '/authorize'
},
};
return require('simple-oauth2').create(credentials);
}
I use that function in this Firebase function that is called using https://us-central1-<my project string>.cloudfunctions.net/redirect:
exports.redirect = functions.https.onRequest((req, res) => {
const oauth2 = spotifyOAuth2Client();
cookieParser()(req, res, () => {
const state = req.cookies.state || crypto.randomBytes(20).toString('hex');
console.log('Setting verification state:', state);
res.cookie('state', state.toString(), {
maxAge: 3600000,
secure: true,
httpOnly: true,
});
const redirectUri = oauth2.authorizationCode.authorizeURL({
redirect_uri: OAUTH_REDIRECT_URI,
//scope: OAUTH_SCOPES,
state: state,
});
console.log('Redirecting to:', redirectUri);
res.redirect(redirectUri);
});
});
The code above returns a url string with the proper parameters, the following code block is where my code breaks, I have another cloud function that runs after being redirected from the res.redirect(redirectUri) above. And when I try to run the getToken() method, it appears to not return anything because I hit the catch block instead? This is where I observe the Auth Token Error Not Found.
const oauth2 = spotifyOAuth2Client();
try {
return cookieParser()(req, res, async () => {
console.log('Received verification state:', req.cookies.state);
console.log('Received state:', req.query.state);
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
console.log('Received auth code:', req.query.code);
console.log(OAUTH_REDIRECT_URI);
// Get the access token object (the authorization code is given from the previous step).
const tokenConfig = {
code: req.query.code,
redirect_uri: 'http://localhost:8100/popup'
};
// Save the access token
try {
const result = await oauth2.authorizationCode.getToken(tokenConfig)
const accessToken = oauth2.accessToken.create(result);
console.log('inside try');
console.log(result);
console.log(accessToken);
} catch (error) {
console.log('Access Token Error', error.message);
}
I've double checked my spotify client/secret credentials in the config, what is going wrong with this OAuth2 flow?
Resolved my issue, I was not using the correct endpoints:
const credentials = {
client: {
id: functions.config().spotify.clientid,
secret: functions.config().spotify.clientsecret,
},
auth: {
tokenHost: 'https://accounts.spotify.com',
authorizePath: '/authorize',
tokenPath: '/api/token'
},
};

Resources