Spotify API demanding a parameter that is not required - flutter

I know this is a particular issue that I'm having but I'm struggling with it for ages...If anyone could give me a hint on how to proceed It would be great.
I'm trying to integrate my flutter app to Spotify API and according to Spotify docs, after the user accepted the conditions at their site, it would be redirected back with a code - In my application I'm already retrieving this code. After that, I need to make a request for another endpoint to "trade" my code for an auth.
The docs demand a POST with 3 parameters(grant_type,redirect_uri and code). But even though I send all of this informations I get a (400 -Bad Request) with the description:
{"error":"invalid_request","error_description":"code_verifier
required"}
But note that code_verifier is only required for a PKCE request, as far as I understand.
My post method is the following:
Future getUserAccessToken(String authorizationCode) async {
// Get the Access Token in exchange for the Authorization Code
var url = 'https://accounts.spotify.com/api/token';
var body = {
'grant_type': 'authorization_code',
'code': authorizationCode,
'redirect_uri': redirectUrl,
};
var response = await post(Uri.parse(url), body: body, headers: {
'Content-Type': "application/x-www-form-urlencoded",
'Authorization':
'Basic ${base64Encode(utf8.encode('$clientID:$clientSecret'))}',
});
}
They also demand this at the authorization header:
Base 64 encoded string that contains the client ID and client secret key. The field must have the format: Authorization: Basic

I would recommend going forward with PKCE. It will protect your app against CSRF and authorization code injection attacks. It's also not too bad to implement.
All you should have to do is:
Generate a random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long. This is your code_verifier
Hash that string with SHA256. This is your code_challenge.
Send those values in the requests as the documentation indicates, which means base64 url encoding them, as well. The Spotify doc says "The body of this POST request must contain the following parameters encoded in application/x-www-form-urlencoded".
Also, in case you haven't seen it, there's a OAuth flow example implemented in JS here: https://github.com/spotify/web-api-auth-examples/blob/master/authorization_code/app.js Their copy of this portion looks like:
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: redirect_uri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
},
json: true
};
request.post(authOptions, function(error, response, body) {...})
The example has "request": "~2.83.0" in the package.json.
Notice how they use form which I would assume is base 64 url encoding the body, as the docs indicate. Not sure how the post method you're using with dart/flutter works with respect to base 64 url encoding, so that's something to investigate, as well.
Credit to https://www.oauth.com/oauth2-servers/pkce/authorization-request/ for providing some of the text in this response

Related

Is anyone knows how Flutter URI encoding/decoding works?

Is anyone knows how Flutter URI encoding/decoding works?
I have one issue. Let me give you some background on the issue. We have one deep link which will give us the email and access token code to reset the password. We will validate the token on the front end and then allow users to do a reset.
Here is the sample URL from Firebase dynamic link
https://example.com/auth/resetPassword/?access_token=abcd&email=test+abc#xyz.com
Firebase dynamic link will give us this URL in the URI object. The issue is when I try to fetch the query params from this URI object, it removes plus sign from an email and replaces it with the space character instead. E-mail should same as displayed in the above link. This is the email received when I fetch query params: test abc#xyz.com
I have created two dart pads to figure out the issue. Here is the 1st sample where I am parsing (since firebase dynamic link is doing the same) the URL into the URI object and tried printing the output. As expected it removes the plus sign.
Code:
main() {
var httpsUri = Uri.parse("https://example.com/auth/resetPassword/?access_token=abcd&email=test+abc#xyz.com");
print(httpsUri.queryParameters);
}
Output:
{access_token: abcd, email: test abc#xyz.com}
In another sample, I tried creating the URI object from the same parameters and link but manually. Here is the code
Code:
main() {
var httpsUri = Uri(
scheme: 'https',
host: 'example.com',
path: '/auth/resetPassword/',
queryParameters: {
'access_token': 'abcd',
'email': 'test+abc#xyz.com'
});
print(httpsUri.queryParameters);
}
Output:
{access_token: abcd, email: test+abc#xyz.com}
If you see here, the email is correctly displayed.
My exact scenario is matching with the first sample code. Based on my findings it removes the plus sign due to encoding and decoding of the URL as a plus sign has a special meaning in the URL. But on the other hand why it is not happening in 2nd example?
Any help would be appreciated!
+ is a reserved character for URIs and therefore should be encoded to %2B if you want a literal + character.
But on the other hand why it is not happening in 2nd example?
Your second example works because it constructs a Uri object directly, and if you were to convert it to a String, it would perform necessary encodings for you. That is, print(httpsUri) would output:
https://example.com/auth/resetPassword/?access_token=abcd&email=test%2Babc%40xyz.com
rather than original (malformed) URL:
https://example.com/auth/resetPassword/?access_token=abcd&email=test+abc#xyz.com

Not able to set generated access_token in request object

I am authenticating user with the help of JWT token and #nestjs/passport library. I am able to successfully generate and return the access token but stuck at how to set it in the req object. So that I can validate other api routes until user logs out
Below code shows how I generated the access_token and returned to the calling point
let payload = { username: user.email, sub: user.id }
const accessToken = this.jwtService.sign(payload);
return {
expires_in: 3600,
access_token: accessToken,
status: 200
}
I am getting the access_token correctly. I verified generated access_token using https://jwt.io/ debugger and results were correct.
Can someone help me in setting up the req object with access_token?
There is multiple way you could send JWT token in your requests. If you have followed this tutorial: https://docs.nestjs.com/techniques/authentication, you are using passport-jwt library for JWT. One way is through header if you don't set auth schema it will be this one:
Authorization: JWT <-HERE YOU SHOULD INSERT TOKEN->
If you set authSchema to be bearer, then you could use this header like this:
Authorization: BEARER <-HERE YOU SHOULD INSERT TOKEN->
Except for that you can put your JWT into URL like this:
https://example.com/me?auth_token=<-HERE YOU SHOULD INSERT TOKEN->
Or in object body that you are sending with property named auth_token:
https://example.com/me
Body:
{
auth_token: <-HERE YOU SHOULD INSERT TOKEN->
}
You can find this in the source code of passport-jwt in this file.

OpenTok Rest Service Invalid JWT Error on Fiddler Request

I'm trying to create OpenTok session by Rest services with JWT object as suggested. I tried to generate session with Fiddler.
Here is my fiddler request (JWT string has been changed with *** partially for security reasons)
POST https: //api.opentok.com/session/create HTTP/1.1
Host: api.opentok.com
X-OPENTOK-AUTH: json_web_token
Accept: application/json
Content-Length: 172
eyJ0eXAiOiJKV1QiL******iOiJIUzI1NiJ9.eyJpc3MiOjQ1NzM******OiJkZW5l******XQiOjE0ODI3OTIzO***SOMESIGNEDKEYHERE***.izvhwYcgwkGCyNjV*****2HRqiyBIYi9M
I got 403 {"code":-1,"message":"Invalid token format"} error probably means my JWT object is not correct. I tried creating it using http://jwt.io (as opentok suggests) and other sites and all seems correct and very similar to the one on tokbox (opentok) site.
I need an explanation to fix it and create a session.
May it be because I am using opentok trial?
JWT creation Parameters
I had the same problem. I resolved the error by setting the correct key-value pairs for the payload part.
Example of my payload is as follows in C#:
var payload = new Dictionary<string, object>()
{
{ "iss", "45728332" },
{ "ist", "project" },
{ "iat", ToUnixTime(issued) },
{ "exp", ToUnixTime(expire) }
};
The value of the "ist" should be set to "project", not the actual name of your project.
Update: Looking at your screenshot, I can say you have not set the secret key (here, it's your ApiKeySecret from TokBox account > project) at the very bottom right.
OK I have found the answer at last,
Your Opentok API Secret key should not be used directly as Sign parameter. In java as shown below, it should be encoded first.
Base64.encodeToString("db4******b51a4032a83*******5d19a*****e01".getBytes(),0)
I haven't tried it on http://jwt.io and fiddler but it seems it will work on it too. Thanks. Full code is below;
payload = Jwts.builder()
.setIssuedAt(currentTime)
.setIssuer("YOUR_OPENTOK_KEY")
.setExpiration(fiveMinutesAdded)
.claim("ist", "project")
.setHeaderParam("typ","JWT")
.signWith(SignatureAlgorithm.HS256, Base64.encodeToString("YOUR_OPENTOK_SECRET".getBytes(),0))
.compact();
return payload;

Upload and Name a File Using Google Drive REST API v3 and Angular 2

I'm creating a Google Drive service using the Drive REST Api v3 in Angular 2. Most of the functionality is in place: view file, download, create etc.. but I cannot find how to name a file (either when creating a file or updating).
I'm using the following docs pages: create and update. They say the file name should be part of the request body. The relevant code from my Google Drive service is bellow.
createFile(name :string, content :string) :Promise<Object> {
let headers = new Headers({
'Content-Type': 'text/markdown',
'Authorization': 'Bearer ' + this.token,
'name': name //TODO name not working!
});
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http
.post('https://www.googleapis.com/upload/drive/v3/files' + '?uploadType=multipart', content, options)
.toPromise();
}
updateFile(id :string, content :string, name :string) :Promise<Object> {
let headers = new Headers({
'Content-Type': 'text/markdown',
'Authorization': 'Bearer ' + this.token,
'id': id,
'name': name //TODO name not working!
}); //generate headers
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http
.patch('https://www.googleapis.com/upload/drive/v3/files/' + id + '?uploadType=multipart', content, options)
.toPromise();
}
To summarise files are being created and updated fine (including content) but naming and renaming a file doesn't work at all.
Thanks for any help.
Try placing name in the request body and not in the request header as described in the Files: create:
Request body
In the request body, supply a Files resource with the following properties as the metadata. For more information, see the document on media upload.
To test it, try using API Explorer to help you explore various Google APIs interactively.
Sample Request:
POST https://www.googleapis.com/drive/v3/files?key={YOUR_API_KEY}
{
"name": "My File"
}
Response:
200
{
"kind": "drive#file",
"id": "fileID",
"name": "My File"
}
There is also a related SO post that explain how to insert file to Google Drive through API.
Hope this helps.
I also faced that problem. I think there is 3 solutions:
Use multipart upload https://developers.google.com/drive/v3/web/multipart-upload with different headers for file metadata and actual file. Me myself stuck there, didn't found how to add boundaries to separate request headers in Angular 2+
Upload file in two requests. First to create empty file with metadata (response will provide id of the file) and second to actually "update" the file.
Use resumable upload. First request to "setup metadata" (will not even create empty file) and get "special link" where to send request to upload actual file. And this approach have some other features, like uploading in chunks.https://developers.google.com/drive/v3/web/resumable-upload
Here is the link to another Question with implementation of resumable upload in Angular 2 and DRIVE REST API V3
Angular 2+ HTTP POST and GDrive API. Resumable file upload with name
I hope it might be useful.
You are trying to set the name using an http header. This is wrong. I can't begin to understand how you thought that was the way to do it, so you need to go back and reread the Drive API documentation.
In short, the name: "name" should be a JSON object passed in the body of the request, not in an http header.

Can we add Json object to RequestHeader of HTTP GET method

I am developing a new REST-full webservice for our application,I wanted to send the reqest data in requestHeader instead of sending as query param, as my request data is large.
I have My jquery code like below to add json to the request header and call the REST service GET method.
$.ajax({
beforeSend: function(req) {
req.setRequestHeader("test", "{name:mouli, id:918}");},
type : "GET",
data :'',
dataType : "jsonp",
url : 'http://localhost:29801/RestFulJSONExample/rest/jobdesc?callback=?',
success : function(data) {
alert("invoked");
}
});
});
And my GET method in my REST service is like
#GET
#Produces("application/javascript")
public JSONWithPadding getJobDescription(#Context HttpHeaders headers) {
List<String> requestHeader = headers.getRequestHeader("test");
//some logic here.
}
i could able to get the JSON object from the request header which i have added in the jquery request.
My question is ..
Can i follow this approach? is it secure and safe?
If not please tell me the other way?
What appears at the right side of the ":" in a header is mostly free. You have to take into account character set restriction in HTTP, and possible carriage returns in the JSON value (you know, headers of more than one line have a specific syntax). If your JSON examples are relatively simple, then I see no problem in that. It is just another way of organizing the actual value of a header line.