Spotify PKCE code_verifier was incorrect - javascript

I was excited to hear that I can now use the Spotify web API without having a backend application via PKCE. Unfortunately, I seem to have some sort of misunderstanding and have been unable to get it to work.
I am likely making some minor mistake along the way, but I did it once to no avail and I wiped the slate clean and tried again but still without luck. From this I gather that I must be misunderstanding the documentation.
I will explain what I am doing and hopefully someone here can point out what I'm missing or doing wrong. I'm assuming I have a fundamental conceptual misunderstanding.
I first generate a cryptographically random string using an npm package called crypto-random-string. I store that in the browser's local storage before using js-sha256 to hash it and then using another npm package called base64url to encode it.
let verifier = cryptoRandomString({length: 50})
window.localStorage.setItem('verifier', verifier)
let params = {
client_id: '[MY CLIENT ID]',
response_type: 'code',
redirect_uri: 'http://localhost:3000/callback',
code_challenge_method: 'S256',
code_challenge: base64url(sha256(verifier))
}
let endpoint = new URL('https://accounts.spotify.com/authorize');
endpoint.search = new URLSearchParams(params);
window.location = endpoint.toString();
From here, I redirect to the /authorize endpoint with the proper url parameters. I have gotten this far successfully and then been redirected accordingly to my provided redirect_uri, where I grab the given code from the url parameters.
At this point, I try the fetch to the /api/token endpoint with the client_id, grant_type, the code I got from the url params, my redirect_uri, and the locally stored code_verifier.
let params = new URLSearchParams(window.location.search);
console.log(params.get('code'));
let newParams = {
client_id: '[MY CLIENT ID]',
grant_type: 'authorization_code',
code: params.get('code'),
redirect_uri: 'http://localhost:3000/callback',
code_verifier: window.localStorage.getItem('verifier')
}
let endpoint = new URL('https://accounts.spotify.com/api/token');
endpoint.search = new URLSearchParams(newParams);
fetch(endpoint.toString(), {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(data => data.json()).then(console.log)
At this point, after both of my attempts I received the error:
{ error: "invalid_grant", error_description: "code_verifier was incorrect" }
Is there anything that I am obviously doing wrong? The error leads me to believe I'm doing something wrong as far as the actual generation of the code_verifier, but I am at a loss to what that issue may be.

Someone on the Spotify forum pointed me to this answer. Not sure why exactly, but doing the encoding the following way does work:
async function sha256(plain) {
const encoder = new TextEncoder()
const data = encoder.encode(plain)
return window.crypto.subtle.digest('SHA-256', data)
}
function base64urlencode(a){
return btoa(String.fromCharCode.apply(null, new Uint8Array(a))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
}
const hashed = await sha256(verifyCode)
const codeChallenge = base64urlencode(hashed)

Previous answers and comments, in addition to OP, has documented most information needed so I will only add what helped me:
The verifier itself most be encoded as a Base64-URL.
Pseduo-code (as I myself code in C#):
verifier = Base64UrlEncode(GetRandomString(length: 50))
challenge = Base64UrlEncode(HashWithSha256(verifier))

Related

Binance API {"code":-1022,"msg":"Signature for this request is not valid."}. Not sure what I am doing incorrectly

I'm not sure what is wrong with this code. I think I might be missing something somewhere but I can't figure it out. I keep getting the error. The keys are for the binance test net and so they aren't important.
const ts = Date.now()
var crypto = require('crypto')
, text = 'recvWindow=59999&timestamp=ts'
, key = 'BqDjQf3F5VNOvSaX1rncMsZqmSOpGTBPz5UgOohto0P3bQlfHvjUlVrkbYc6pwc3'
, hash;
var hash = crypto.createHmac('sha256', key).update(text).digest('hex');
[hash, key, ts.toString()];
Not sure what you're doing wrong exactly, I'm not familiar with the crypto package you're using (maybe your secret is missing?), but here's an example of a correctly formatted request that fetches your trades in the BTC/USDT market
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("X-MBX-APIKEY", "...");
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch("https://api.binance.com/api/v3/myTrades?symbol=BTCUSDT&limit=500&timestamp=1642400013504&signature=...", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
I got this from using binance's postman examples repo where they have a bunch of pre set-up postman HHTP request in postman, where you can enter your api details into an environment
and then open an example, and copy the relevant code in any language that you want
You have to link the environment you created to your example also, I don't remember the exact process, but I don't think it's difficult
You can also get around all the signature configuring by using CCXT, it's kinda like an SDK with a unified response for requests to about 120 crypto exchanges. Here's how you would make a request with CCXT
const ccxt = require("ccxt");
const config = require("../config.json");
const binance = new ccxt.binance(config.binance);
// binance.verbose = true;
const trades = binance.fetch_my_trades("BTC/USDT");
console.log(trades)
CCXT also has a number of examples in the repo at the path ccxt/examples
P.S. Delete that Api Key, and get a new one. In the future just post your api key like '...' or my_api_key or something, as well as your secret and signature

How do I complete authentication using the Spotify Web API

I'm trying to generate an access token to access the web player of the SpotifyWebApi and play music in the browser. Even after trying several things, including reading Spotify's authorization documentation for the Authorization Code Flow, I'm still stuck and I can't get an access code. The documentation outlines a 3-step process. Only the first part is working for me, and I'm stuck in the 2nd step. (Spotify Docs: https://developer.spotify.com/documentation/general/guides/authorization-guide/)
Here's what I've done:
Step 1:
export const authEndpoint = "https://accounts.spotify.com/authorize";
const redirectUri = "http://localhost:3000/";
const clientID = "[redacted]";
const clientSecret = "[redacted]";
const scopes = [
"user-library-read",
"user-library-modify",
"user-read-playback-state",
"user-read-currently-playing",
"user-read-recently-played",
"user-top-read",
"user-modify-playback-state"
];
export const loginUrl = `${authEndpoint}?client_id=${clientID}&response_type=code&redirect_uri=${redirectUri}&scopes=${scopes.join(
"%20"
)}&show_dialog=true`;
const ACCESS_CODE = getCodeFromUrl();
Step 2:
const POST_OBJECT_BODY = {
grant_type: "authorization_code",
code: ACCESS_CODE,
redirect_uri: redirectUri,
client_id: clientID.toString("base64"),
client_secret: clientSecret.toString("base64"),
};
fetch("https://accounts.spotify.com/api/token", {
mode: "no-cors",
method: "POST",
body: JSON.stringify(POST_OBJECT_BODY),
}).then((response) => {
console.log("Response: ", response);
});
I had to add the "no-cors" part because I was getting CORS errors.
I've tried other authentication methods and they seem to work, but I'm not getting a proper access token. I say this because I'm unable to play the music - it kept saying "Permissions missing" despite me having the proper scopes.
Any help with the above will be appreciated. Thank you!
Just a small remark, the redirect uri you are using in step 2 has to be encoded.

Use git credential manager to fetch azure devops api instead of personal access token

I am trying to fetch git azure devops api to get information about repositories and branches in js.
In order to achieve that, I made a little application with the following code :
$(document).ready(function() {
var personalToken = btoa(':'+'<personnalAccessToken>');
fetch('https://dev.azure.com/<company>/<project>/_apis/git/repositories?api-version=5.1', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
'Authorization': 'Basic '+ personalToken
}
}).then(function(response) {
return response.json();
}).then(function(repositories) {
console.log("There are "+repositories.count+" repositories");
}).catch(function(error) {
console.log('Fetch error: ' + error.message);
});
This code is working great but as you can see there is my personnalAccessToken writen directly inside the code... which is really bad...
When I am using git in command line, I don't have to specify any credential information because I use git credential manager for windows. Which means my personnalAccessToken is already stored, cached and automatically used everytime I use a git command, like clone, etc.
So, I would like my js code to use the same thing, I would like it to use my stored credentials automatically to fetch the api without being required to set my personnalAccessToken in code.
I have already searched for hours but can't find out if it is possible.
I have already searched for hours but can't find out if it is
possible.
Sorry but as I know it's impossible. The way you're calling the Rest API is similar to use Invoke-RestMethod to call rest api in Powershell.
In both these two scenarios, the process will try to fetch PAT for authentication in current session/context and it won't even try to search the cache in Git Credential Manager.
You should distinguish the difference between accessing Azure Devops service via Rest API and by Code:
Rest API:
POST https://dev.azure.com/{organization}/{project}/{team}/_apis/wit/wiql?api-version=5.1
Request Body:
{
"query": "Select [System.Id], [System.Title], [System.State] From WorkItems Where [System.WorkItemType] = 'Task' AND [State] <> 'Closed' AND [State] <> 'Removed' order by [Microsoft.VSTS.Common.Priority] asc, [System.CreatedDate] desc"
}
Corresponding Code in C#:
VssConnection connection = new VssConnection(new Uri(azureDevOpsOrganizationUrl), new VssClientCredentials());
//create http client and query for resutls
WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
Wiql query = new Wiql() { Query = "SELECT [Id], [Title], [State] FROM workitems WHERE [Work Item Type] = 'Bug' AND [Assigned To] = #Me" };
WorkItemQueryResult queryResults = witClient.QueryByWiqlAsync(query).Result;
Maybe you can consider using a limited PAT, limit its scope to Code only:
I know there exists other Authentication mechanism
:
For Interactive JavaScript project: ADALJS and Microsoft-supported Client Libraries.
You can give it a try but I'm not sure if it works for you since you're not using real Code way to access the Azure Devops Service... Hope it makes some help :)
If you have the script set up in an Azure Runbook you can set it as an encrypted variable there and have it pull it from there before running rather than having it directly written into the code.
$encryptedPatVarName = "ADO_PAT"
$adoPat = Get-AutomationVariable -Name $encryptedPatVarName
$adoPatToken = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($adoPat)"))
$adoHeader = #{authorization = "Basic $adoPatToken"}
The above is the Powershell version of it. I have seen some people do it with other

How to get "Sign in with Twitter" working with NodeJS

Im trying to implement Sing In with Twitter working, however i keep getting the following error
Whoa there!
The request token for this page is invalid. It may have already been used, or expired because it is too old. Please go back to the site or application that sent you here and try again; it was probably just a mistake.
Here's my code, the what am I doing wrong?
// Server
var OAuth = require('oauth').OAuth;
var callbackUrl = null;
var oa = new OAuth(
'https://api.twitter.com/oauth/request_token',
'https://api.twitter.com/oauth/access_token',
CONSUMER_KEY,
CONSUMER_SECRET,
'1.0',
callbackUrl,
'HMAC-SHA1'
);
const promise = new Promise((resolve, reject) => {
oa.getOAuthRequestToken(function(error, token, secret, results) {
resolve({
token,
secret,
});
});
});
var oauth = await promise;
return { oauth };
// Client
window.open(`https://api.twitter.com/oauth/authenticate?oauth_token=${twitter.oauth.token})`);
I've also set the callback url to the same as what I have in the app details page, but no dice!
var callbackUrl = 'http://0.0.0.0:3001/settings/social/twitter/oauth';
Small note, not sure if it helps, but I've noticed the returned tokens are shorter than the examples I've seen online. Example:
Mine: EBhy3AAAAAAA94TPAAABajDwCww%
Twitter Site: NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0&
My client code had an extra ")"
window.open(`https://api.twitter.com/oauth/authenticate?oauth_token=${twitter.oauth.token})`);
should read
window.open(`https://api.twitter.com/oauth/authenticate?oauth_token=${twitter.oauth.token}`);

how to use Cryptocapital API v4, can't find sdk download link

Cryptocapital.co provides API to exchange crypto currency, I want to access their API to integrate with a website, they do have a documentation here - https://api.cryptocapital.co/v4
and they have a sample code as below
var key = '1234567890abcdef';
var secret = '1234567890abcdef';
var command = 'PING';
var nonce = Date.now();
var message = command + nonce;
var signature = CryptoJS.SHA1(message + key + secret);
var options = {
url: 'https://api.cryptocapital.co/v4/ping',
headers: {
'key': key,
'message': message,
'signature': signature,
'nonce': nonce
}
};
request(options, function(err, res, body) {
// do something
// ...
});
There is no download link or reference to any SDK, when i run this code it says
request is not defined
I don't know where to start in this specific API.
please see the documentation and help me on identifying what I am doing wrong.
It's making the assumption that your using the popular request lib to make the HTTP request.
You can, however, use whatever library you like. Or don't use a library at all, use https.get.

Categories

Resources