I am extracting the authorization code from the URL http://localhost:/api/auth/socialRedirectLoginUrl/facebook?code=AQ[...]cmLRa5iox2HVl4VKd6sacvW7VmjygY4ydF8eGpuNjXzBnxRXTBYi29EhSPyBEUf3-8BPAIG3_9b_6OBIQ#_=_ to my backend.
After this I fetched the query from the Url in my backend using this
#Redirect("", 302)
#Get("socialRedirectLoginUrl/:type")
async socialRedirectLoginUrl(
#Query("code") code: string,
)
I am getting the code in my backend but it it not proceeding further. I am getting an HTTP status 400 on calling this API.
if (type == "facebook") {
const { data } = await axios({
url: "https://graph.facebook.com/v4.0/oauth/access_token",
method: "get",
params: {
client_id: process.env.APP_ID,
client_secret: process.env.APP_SECRET,
redirect_uri:
"http://localhost:8084/api/auth/socialRedirectLoginUrl/facebook",
code,
},
});
}
Error screenshot of backend
Front end code:
function getFacebookAuthEndpoint() {
const facebookStringifiedParams = queryString.stringify({
client_id: process.env.REACT_APP_FACEBOOK_APP_ID,
redirect_uri: `${process.env.REACT_APP_PUBLIC_URL_ADMIN_API}/api/auth/socialRedirectLoginUrl/facebook`,
scope: ["email", "user_friends"].join(","), // comma seperated string
response_type: "code",
auth_type: "rerequest",
display: "popup",
});
const facebookAuthEndpoint = `https://www.facebook.com/v4.0/dialog/oauth?${facebookStringifiedParams}`;
return facebookAuthEndpoint;
}
openWindow(getFacebookAuthEndpoint())
Related
I'm working through this tutorial on creating an app that uses the Spotify API. Everything was going great until I got to the callback portion of authenticating using the authentication code flow.
(I do have my callback URL registered in my Spotify app.)
As far as I can tell, my code matches the callback route that this tutorial and others use. Significantly, the http library is axios. Here's the callback method:
app.get("/callback", (req, res) => {
const code = req.query.code || null;
const usp = new URLSearchParams({
code: code,
redirect_uri: REDIRECT_URI,
grant_type: "authorization_code",
});
axios({
method: "post",
url: "https://accounts.spotify.com/api/token",
data: usp,
headers: {
"content-type": "application/x-www-form-urlencoded",
Authorization: `Basic ${new Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64")}`,
},
})
.then(response => {
console.log(response.status); // logs 200
console.log(response.data); // logs encoded strings
if (response.status === 200) {
res.send(JSON.stringify(response.data))
} else {
res.send(response);
}
})
.catch((error) => {
res.send(error);
});
Though the response code is 200, here's a sample of what is getting returned in response.data: "\u001f�\b\u0000\u0000\u0000\u0000\u0000\u0000\u0003E�˒�0\u0000Ee�uS\u0015��\u000e�(\b\u0012h\u0005tC%\u0010\u0014T\u001e�����0��^:���p\u0014Ѻ\u000e��Is�7�:��\u0015l��ᑰ�g�����\u0"
It looks like it's encoded, but I don't know how (I tried base-64 unencoding) or why it isn't just coming back as regular JSON. This isn't just preventing me logging it to the console - I also can't access the fields I expect there to be in the response body, like access_token. Is there some argument I can pass to axios to say 'this should be json?'
Interestingly, if I use the npm 'request' package instead of axios, and pass the 'json: true' argument to it, I'm getting a valid token that I can print out and view as a regular old string. Below is code that works. But I'd really like to understand why my axios method doesn't.
app.get('/callback', function(req, res) {
// your application requests refresh and access tokens
// after checking the state parameter
const code = req.query.code || null;
const state = req.query.state || null;
const storedState = req.cookies ? req.cookies[stateKey] : null;
res.clearCookie(stateKey);
const authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: REDIRECT_URI,
grant_type: 'authorization_code',
},
headers: {
Authorization: `Basic ${new Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64')}`,
},
json: true,
};
request.post(authOptions, function (error, response, body) {
if (!error && response.statusCode === 200) {
const access_token = body.access_token;
const refresh_token = body.refresh_token;
var options = {
url: 'https://api.spotify.com/v1/me',
headers: { Authorization: 'Bearer ' + access_token },
json: true,
};
// use the access token to access the Spotify Web API
request.get(options, function(error, response, body) {
console.log(body);
});
// we can also pass the token to the browser to make requests from there
res.redirect('/#' + querystring.stringify({
access_token: access_token,
refresh_token: refresh_token,
}));
} else {
res.redirect(`/#${querystring.stringify({ error: 'invalid_token' })}`);
}
});
});
You need to add Accept-Encoding with application/json in axios.post header.
The default of it is gzip
headers: {
"content-type": "application/x-www-form-urlencoded",
'Accept-Encoding': 'application/json'
Authorization: `Basic ${new Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64")}`,
}
I'm using the Nuxt auth module v5 and the Laravel sanctum provider. My csrf-cookie route works fine, and my login route works fine, but when trying to call this.$axios from a function, such as when creating a user's account (since auth module doesn't offer this) I'm getting a CSRF token mismatch.
It would appear that using axios directly like this doesn't have access to setting the cookie since no user logged in, how can I get the cookie to be set?
Method for account creation
/*
** Create accounr
*/
createAccount () {
this.feedback.isShown = false
this.isCreatingAccount = true
if (this.apiAccountCreationSource) this.apiAccountCreationSource.cancel('aborted')
const CancelToken = this.$axios.CancelToken
this.apiAccountCreationSource = CancelToken.source()
this.$axios.post(`${this.$config.apiUrl}/api/account`, this.account, {
cancelToken: this.apiAccountCreationSource.token,
timeout: 30 * 1000
}).then(res => {
this.apiAccountCreationSource = null
this.setContextualResponse(res)
setTimeout(() => {
this.login()
}, 250)
}).catch(err => {
this.setContextualResponse(err.response ? err.response.data : null)
}).finally(() => {
this.isCreatingAccount = false
})
},
Nuxt config
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
credentials: true,
baseURL: process.env.API_DOMAIN
},
// Auth module configuration: https://auth.nuxtjs.org/
auth: {
redirect: {
login: '/account/login/',
logout: '/account/login/',
callback: '/account/login/',
home: '/account/dashboard/'
},
strategies: {
'laravelSanctum': {
provider: 'laravel/sanctum',
url: process.env.API_DOMAIN,
endpoints: {
login: { url: '/api/login', method: 'post' },
logout: { url: '/api/account/logout', method: 'post' },
user: { url: '/api/account', method: 'get', propertyName: 'user' }
}
}
}
},
If you need to get the CSRF token all you need to do is make a request to your token endpoint and your browser should save the XSRF token. Then axios will automatically send this token in every subsequent request.
So all that you need to do is make a axios GET request to your csrf-cookie route before you send your POST request.
this.$axios.get(`${this.$config.apiUrl}/sanctum/csrf-cookie`)
Or you can chain both requests doing something like this:
this.$axios.get(`${this.$config.apiUrl}/sanctum/csrf-cookie`).then(() => {
return this.$axios.post(`${this.$config.apiUrl}/api/account`, this.account, {
cancelToken: this.apiAccountCreationSource.token,
timeout: 30 * 1000
}).then((res) => {
this.apiAccountCreationSource = null
this.setContextualResponse(res)
setTimeout(() => {
this.login()
}, 250)
}).catch((err) => {
this.setContextualResponse(err.response ? err.response.data : null)
}).finally(() => {
this.isCreatingAccount = false
})
})
Your authentication strategy works without this hassle because it handles this csrf request internally (https://github.com/nuxt-community/auth-module/blob/dev/src/providers/laravel-sanctum.ts)
References:
https://laravel.com/docs/8.x/sanctum#csrf-protection
https://github.com/axios/axios/issues/708#issuecomment-280920224
I'm building a serverless app using NextJs and I'm stuck on how I can get a return value that includes a user's JWT when making a post request to my DB. Everything works as expected but I can't get this one thing down.
In my login page, this is how I handle user-submitted data:
const res = await fetch(`${process.env.BASE_URL}/api/login`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(userInfo)
})
console.log('successfully logged in')
console.log(res)
In my API, the way I return a value when making a request to /api/login is:
return res.status(201).json({
message: 'Account successfully signed in!',
user,
token
})
Now when I go to check my console.log in dev tools, here's what the response looks like:
type: "basic"
url: "http://localhost:3000/api/login"
redirected: false
status: 200
ok: true
statusText: "OK"
headers: Headers {}
body: ReadableStream
locked: false
__proto__: ReadableStream
bodyUsed: false
__proto__: Response
Nothings there and I don't know why?
This is a fetch body response, you should decode it by the type that you are expecting (in your case json).
const res = await fetch(`${process.env.BASE_URL}/api/login`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(userInfo)
})
const data = await res.json(); // <--- this is the missing part
console.log('successfully logged in')
console.log(data)
working on connecting users to google, and we're trying to get their access and refresh tokens from the google api, and we're getting an issue exchanging the OAuth2 Code for tokens. Both sets of code have the same error.
I initialize the gapi client and fill in the information needed like so:
gapi.load('client:auth2', _ => {
gapi.client.init({
'apiKey': 'omitted for security',
clientId: 'omitted for security',
'scope': 'https://www.googleapis.com/auth/drive',
'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest']
}).then(_ => {
gapi.auth2.getAuthInstance().grantOfflineAccess().then(resp => {
if(resp.code){
gapi.client.request({
path: 'https://www.googleapis.com/oauth2/v4/token',
method: 'post',
params: {code: resp.code},
body: {
code: resp.code,
client_id: opts.clientId,
client_secret: 'omitted for security',
grant_type: 'authorization_code',
redirect_uri: 'omitted for security',
access_type: 'offline'
},
}).then((onfulfill, onreject, context) => {
console.log('fulfilled', onfulfill);
console.log('rejected: ', onreject);
console.log('context', context);
}).catch(err => console.error(err.body));
}
});
});
});
What I'm trying to do in the .then() is to call the token endpoint to exchange the code in the response for a refresh and access token to store in my back end and the user's local storage.
I get this error response from both versions of the code. (better, more reliable code is provided here.)
{ "error": "redirect_uri_mismatch", "error_description": "Bad
Request" }
I also have a backend setup stashed as a last resort that accepts the code from gapi.auth2.getAuthInstance().grantOfflineAccess() calls the token endpoint, and returns the access_token and refresh_token to the client.
This code is similar, but not quite. instead of using the google api library, I used fetch, and it works fine. (Fetch and XHR on the front end have the same issues as the gapi.client.request function....)
const gConfig = require('./basic.json');
const scopes = ['https://www.googleapis.com/auth/drive'];
const { client_id, client_secret, redirect_uris } = gConfig.web;
const authClient = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
app.post('/', (req, res) => {
const { code } = req.body;
console.log('Received Code From Request: ', code);
let data = { code , client_id, client_secret,redirect_uri: redirect_uris[0], grant_type: 'refresh_token'};
let encodedParams = Object.keys(data).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(data[k])).join('&');
fetch(
`https://www.googleapis.com/oauth2/v4/token?code=${code}`,
{ method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: encodedParams }
).then((res) => {
console.log('called the api with fetch');
console.dir(res.json());
});
authClient.getToken(code, (err, token) => {
if (err) {
console.error(err);
res.status(500).json(err);
}
// console.dir(token);
console.log('TOKEN: =>', token);
res.json(token);
});
});
Is there anyone that's done this on the front end successfully?
You can't get a refresh token in a browser. Your example code would only work on a server. To do oauth at the client you should request "token" instead of "code".
I have a problem with sending an ajax request for the authentification..
i dont get errors but Network in chrome says : method : get , status : canceled , type : pending
and no response from this request ..
when i click on the file connection.js it point in this line :
// start the request!
xhr.send(requestOptions.data);
& my path name and the method get have the color RED
here is my code :
Ext.onReady(function() {
Ext.Ajax.request({
url: 'https://api.mysite.com/api/oauth/',
method: 'GET',
useDefaultXhrHeader:false,
disableCaching: false,
timeout:120000,
params: {
client_id: 'xxxxxx',
client_secret: 'xxx',
format: 'json'
},
success: function(response) {
var resultat = Ext.JSON.decode(response.responseText);
//the response is : {"status":"ok","auth_token":"xxxxxxxxxxx"}
if (resultat.status === "ok") {
if (!resultat.access_token === "") {
access_token = resultat.access_token;
me.sessionToken = resultat.sessionToken;
}
else
{
new Ext.Ajax.request({
url: 'https://api.mysite.com/api/oauth/signin',
method: 'post',
params: {
username: username,
password: password,
authtoken: resultat.access_token,
format: 'json'
},
success: function(response) {
var loginResponse = Ext.JSON.decode(response.responseText);
if (loginResponse.success === "true") {
// The server will send a token that can be used throughout the app to confirm that the user is authenticated.
me.sessionToken = loginResponse.sessionToken;
me.signInSuccess(); //Just simulating success.
} else {
me.signInFailure(loginResponse.message);
}
},
failure: function(response) {
me.sessionToken = null;
me.signInFailure('Login failed. Please try again later.');
}
});
}
// The server will send a token that can be used throughout the app to confirm that the user is authenticated.
} else {
//exception
}
}
,
failure: function(response) {
me.sessionToken = null;
Ext.Msg.alert('failed !!'); // its what it shows me
}
});