I am trying to optain a token from Django - OauthToolkit but I only get the "unsupported_grant_type" error:
Here is what I have writen in react-native:
async getToken (client_id, client_key, username, password) {
let response = await fetch('https://example.com/o/token/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
body: JSON.stringify({
'client_id': client_id,
'client_secret': client_key,
'grant_type': 'password',
'username': username,
'password': password,
})
})
let responseJson = await response.json()
var token = responseJson.error <- written to see the error (shoul be responseJson.acces_token)
this.setState({token})
}
other posts mentioned it could be an error in the headers - but it leaves me clueless right now.
After scratching my head and countless google searches, here's how I did it.
allow me to make some assuptions
Assume your backend server works fine, and any endpoints are protected.
Assume when you go to access an endpoint you get the error
"detail": "Authentication credentials were not provided."
Assume you can authenticate/get access token from postman or by sending a POST request to /o/token with the parameters
username
password
client_id
grant_type
With django-oauth-toolkit, it's crucial to send the data/body as
'Content-Type': 'application/x-www-form-urlencoded'
Note: My approach might not be neat so I welcome any constructive criticism/advise
import { AUTH_LOGIN} from 'react-admin';
var _client_id = 'xxxxxxxxx';
var _grant_type = 'password';
export default (type, params) => {
if (type === AUTH_LOGIN) {
const {username, password } = params;
let _data = "grant_type="+_grant_type+"&username="+username+"&password="+password+"&client_id="+_client_id
const request = new Request('http://localhost:8000/api/ps/o/oauth/token/', {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded',}),
body : _data,
})
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ access_token }) => {
localStorage.setItem('token', access_token);
});
}
return Promise.resolve();
}
Finally yet importantly, /o/token returns a dictionary with key 'access_token' and not token. As such, modify your code as highlighted below
.then(({ access_token }) => {localStorage.setItem('token', access_token);});
Related
This is my JS code for the API
export const getUser = async (user) => {
//Working
const json = await fetch( "*****/username/getUser", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
user:user
}),
})
.then((res) => {
return res.json();
})
.catch((err) => {
console.log("Error in getUser: " + err);
});
return json;
};
Here is an attempt to make the request, which unfortunately return the authentication error.
fetchUser.request("kirolosM")
.then((result)=>{
console.log(result);
}).catch((err)=>{console.log("Error ",err);})
The error
{
"message": "Missing Authentication Token"
}
I have tested the API using postman and it is working as expexted.
Probably useful to compare the request sent from your json code to the one you're sending from postman. It looks like you need to include you auth token in your headers in your request.
Something like
export const getUser = async (user) => {
//Working
const json = await fetch( "*****/username/getUser", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authentication": `Bearer ${token}`
},
...
let tokenUrl = 'https://my.url/oauth2/token';
let scope = 'pets/read pets/updage petId/read'
let getTokenRequest = {
method: 'POST',
url: tokenUrl,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'Basic Base64Encode(client_id:client_secret)'}, // encoded manually beforehand
body: {
mode: 'formdata',
formdata: [
{ key: 'grant_type', value: 'client_credentials' },
{ key: 'scope', value: scope }
]
}
};
pm.sendRequest(getTokenRequest, (err, response) => {
let jsonResponse = response.json(),
newAccessToken = jsonResponse.access_token;
pm.environment.set('access_token', newAccessToken);
pm.variables.set('access_token', newAccessToken);
});
Geeks, help, please!
I have API with Cognito authorization (Client Credentials type). It work's fine in Postman with manually 'Request new Access token'. But I want to retrieve token with pre-request script. I relied on AWS documentation about token endpoint. I have
JSONError: No data, empty input at 1:1
in the console. Do you have any suggestions?
I've encountered the same problem and resolved it by setting grant_type and scope as query string in the url:
let tokenUrl = pm.variables.get("cognito-url")+ "/oauth2/token?grant_type=client_credentials&scope=' + pm.variables.get("cognito-scope");
let auth_code = btoa(pm.variables.get("cognito-client-id") + ":" + pm.variables.get("cognito-client-secret"))
let getTokenRequest = {
method: 'POST',
url: tokenUrl,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + auth_code}
};
pm.sendRequest(getTokenRequest, (err, response) => {
let jsonResponse = response.json(),
newAccessToken = jsonResponse.access_token;
pm.environment.set('access_token', newAccessToken);
pm.variables.set('access_token', newAccessToken);
});
Not sure why django won't accept my POST request for an access token. All my parameters are correct and I already have the authorization code, but a follow-up POST request for the access token gives me this error.
Content-type is correct from what I read from others. If the pkce side was non-accurate it would give me a more specific error about that.
HttpErrorResponse is
{ error: "unsupported_grant_type" } 400 bad request
requestToken(code: string, state: string) {
const clientState = sessionStorage.getItem('pkce-state');
if (clientState !== state) {
console.error('States do not match!');
}
const verifier = sessionStorage.getItem('pkce-verifier');
const params = new URLSearchParams({
grant_type: 'authorization_code',
redirect_uri: 'http://localhost:4200/dashboard',
client_id: 'client_id',
code,
state,
verifier
});
return this.http.post('http://localhost:8000/o/token/',
{
params
},
{
withCredentials: true,
headers: new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded'
}
)
});
}
Also tried this:
requestToken(code: string, state: string) {
const clientState = sessionStorage.getItem('pkce-state');
if (clientState !== state) {
console.error('States do not match!');
}
const verifier = sessionStorage.getItem('pkce-verifier');
return this.http.post('http://localhost:8000/o/token/',
{
grant_type: 'authorization_code',
redirect_uri: 'http://localhost:4200/dashboard',
client_id: 'client_id',
code,
state,
verifier
},
{
withCredentials: true,
headers: new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
}
)
});
}
Try matching step 7 of my write up precisely:
Use code_verifier
Get rid of the state parameter, which is not needed on a direct https message to the token endpoint
Error messages are often misleading but this will make your message 100% standard and hopefully will work.
Then again, always possible that Django doesn't support this flow properly ...
I'm trying to implement post webservice in angular2.
I have tried to hit the URL through postman and its working. But when i'm trying to implement it in angular, I'm not getting any response.
The following is my code sample:
load(username, password) {
console.log(username," ", password);
let postData = {"username":username, "password" : password,
"userdeviceInfo": [{
"deviceId": "APA91bGUZuKVbqur7Qq2gy2eyomWgXkIU5Jcmmtmgl4IGuzVzwiJVMZgAHj3Bx6yrnW0oEZlEtB9XdcR6AOpKyEMVSWwQ_UIfNX6T0iwq28hnufOhauVdTYZQSWWPAdDrdg58cjnL5T-",
"platform":"Android"
}]};
//let body= JSON.stringify(postData);
//console.log("body---"+body)
this.headers = new Headers();
this.headers.append("Content-Type", 'application/json');
this.requestoptions = new RequestOptions({
method: RequestMethod.Post,
url: this.url,
headers: this.headers,
body: JSON.stringify(postData)
})
console.log("body---"+this.requestoptions)
return this.http.request(new Request(this.requestoptions))
.map((res: Response) => {
if (res) {
console.log(res.json());
return [{ status: res.status, json: res.json() }];
}})
.subscribe(res => this.data = res);
the error i'm recieving is:
XMLHttpRequest cannot load "MY_URL". Response for preflight has invalid HTTP status code 500
I'm kind of stuck here. Can anyone help me find where am i going wrong?
here is a POST example:
rate(url: string, body: { value: number}) {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(url, body, options).toPromise().then(
response => response.json(),
err => err
);
}
Of course you can delete toPromise() to use observables, this is for an example app :)
hope this can help you.
You can use this way to make a http post :
let headers = new Headers();
let body = {"username":username, "password" : password,
"userdeviceInfo": [{
"deviceId": "APA91bGUZuKVbqur7Qq2gy2eyomWgXkIU5Jcmmtmgl4IGuzVzwiJVMZgAHj3Bx6yrnW0oEZlEtB9XdcR6AOpKyEMVSWwQ_UIfNX6T0iwq28hnufOhauVdTYZQSWWPAdDrdg58cjnL5T-",
"platform":"Android"
}]};
headers.append('content-type', 'application/json');
return this.http.post("theurl", '', {
body : JSON.stringify(body),
headers : headers
})
.map(res => res.json())
.subscribe(data=>{
},
err=>{
},
()=>{
})
I've been trying to implement some authentication component in my app for a few hours now, and I still don't understand some of the things that are happening.
Basically, I'd like to send a POST request containing some credentials to my API, which sends me a cookie back with a token if the credentials worked. Then, the cookie should be included in the headers of all future requests to my API (which I believed was automatic).
server.js (my API is a mockup for now, with JSON files)
...
app.post('/api/login', jsonParser, (req, res) => {
fs.readFile(ACCOUNTS_FILE, (err, data) => {
if (err) {
console.error(err);
process.exit(1);
}
const accounts = JSON.parse(data);
const credentials = {
email: req.body.email,
password: req.body.password,
};
var token = null;
for (var i = 0; i < accounts.length; ++i) {
const account = accounts[i];
if (account.email === credentials.email
&& account.password === credentials.password) {
token = account.token;
break;
}
}
if (token) {
res.setHeader('Set-Cookie', `access_token=${token}; Secure; HttpOnly;`);
res.json({ token });
} else {
res.json({ token: null });
}
});
});
...
app.js
...
handleConnection(e) {
e.preventDefault();
const email = this.state.email.trim();
const password = this.state.password.trim();
if (!email && !password) {
return (false);
}
fetch(loginUrl, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
credentials: 'include',
},
body: JSON.stringify(this.state),
})
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.warn(error);
});
return (true);
}
...
Now the console.log(data) always displays my token (or null if my credentials are wrong), but the cookie thing doesn't work...
See, I receive the Set-Cookie header, but I still have no cookie on my page.
And even if I managed to get the cookie, when I try to create a cookie using document.cookie = "access_token=123"; and then send the request again, my cookie doesn't go in my header like it would with a jQuery Ajaxcall :
I read here that adding credentials: 'include' would save the day, but unfortunately it didn't.
What am I missing here?
Thanks in advance!
I had the same problem and I found the answer in Peter Bengtsson's comment here: https://davidwalsh.name/fetch
If I understood, in your case the fetch should be:
fetch(loginUrl, {
credentials: 'same-origin',
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(this.state),
})