Axios post request to Firebase Auth REST API produces 400 error - firebase

I have an instance of Axios:
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://identitytoolkit.googleapis.com/v1'
});
export default instance;
Then I import it in my signup.vue file:
<script>
import axios from '../../axios-auth';
...
</script>
In that Vue file I have a signup form, which runs the following method once I hit the Submit button:
onSubmit() {
const formData = {
email: this.email,
age: this.age,
password: this.password,
confirmPassword: this.confirmPassword,
country: this.country,
hobbies: this.hobbyInputs.map(hobby => hobby.value),
terms: this.terms
};
console.log(formData);
axios.post('/accounts:signUp?key=my_key_goes_here', {
email: formData.email,
password: formData.password,
returnSecureToken: true
})
.then(res => {
console.info(res);
})
.catch(error => {
console.error(error);
});
}
I'm getting a 403 error - forbidden 400 error - bad request.
I tried to change headers:
instance.defaults.headers.post["Access-Control-Allow-Origin"] = "localhost";
instance.defaults.headers.common["Content-Type"] = "application/json";
But that didn't help.
I'm working from localhost and I saw that localhost is allowed by default. I tried also to add 127.0.0.1 to the list, but that also didn't help.
What am I missing? How can I make this request work?

If you get a 400 error it is maybe because you get an error from the API itself:
Common error codes
EMAIL_EXISTS: The email address is already in use by another account.
OPERATION_NOT_ALLOWED: Password sign-in is disabled for this project.
TOO_MANY_ATTEMPTS_TRY_LATER: We have blocked all requests from this device due to unusual activity. Try again later.
As a matter of fact, those errors return an HTTP Status Code of 400.
You can see the exact response message (e.g. EMAIL_EXISTS) by doing the following with axios:
axios.post('/accounts:signUp?key=my_key_goes_here', {
email: formData.email,
password: formData.password,
returnSecureToken: true
})
.then(res => {
console.info(res);
})
.catch(error => {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
} else if (error.request) {
console.log(error.request);
} else {
console.log("Error", error.message);
}
});
See https://github.com/axios/axios#handling-errors

I agree with you as i have tried many approaches but was not getting the result. Hence i have tried to change the code.
You need to make two changes in your code.
1] You need to comment the instance.defaults.headers.post["Access-Control-Allow-Origin"] = "localhost"; because you are providing the authentication globally. As, firebase provides the feature of authentication and you are connecting the web app with REST API.
2] You need to add { headers: {'Content-Type': 'application/json' } in the axios.post() method to prevent it from CORS Error.
Following this approach i hope you can get the respective output.
Happy Coding!

Directly call
https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[yourkey]
No need to keep it in a separate file

Anyone who comes to the thread in future. I faced this issue and lost in debugging and worked with fetch. It was tiresome and took me a day but i made axios work. Here is the code.
const data = JSON.stringify({
idToken: authContext.token,
password: enteredNewPassword,
returnSecureToken: false,
});
// Send the valid password to the endpoint to change password
axios
.post(
"https://identitytoolkit.googleapis.com/v1/accounts:update?key=[Your Key]",
data,
{
headers: {
"Content-Type": "application/json",
},
}
)
.then((response) => {
console.log(response.data);
})
.catch((err) => {
console.log(err.message);
});
Remember to Stringify the data you want to send. Stringify it outside of the http request and then pass that variable. Don't know why but this helps!
Lastly remember to add the header when sending the request to firebase. Make sure axios.post is on the same line. My formatter gave a line break which was also cause of error.
Hope it helps :)

Related

How to generate billing portal link for Stripe NextJS with Firebase extension?

I'm using the Stripe extension in Firebase to create subscriptions in a NextJS web app.
My goal is to create a link for a returning user to edit their payments in Stripe without authenticating again (they are already auth in my web app and Firebase recognizes the auth).
I'm using the test mode of Stripe and I have a test customer and test products.
I've tried
The Firebase Stripe extension library does not have any function which can just return a billing portal link: https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-web-sdk/markdown/firestore-stripe-payments.md
Use the NextJS recommended import of Stripe foudn in this Vercel blog
First I setup the import for Stripe-JS: https://github.com/vercel/next.js/blob/758990dc06da4c2913f42fdfdacfe53e29e56593/examples/with-stripe-typescript/utils/get-stripejs.ts
export default function Settings() {
import stripe from "../../stripe_utils/get_stripejs"
async function editDashboard() {
const dashboardLink = await stripe.billingPortal.sessions.create({
customer: "cus_XXX",
})
}
console.log(dashboardLink.url)
return (
<Button
onClick={() => editDashboard()}>
DEBUG: See payments
</Button>
)
}
This would result in an error:
TypeError: Cannot read properties of undefined (reading 'sessions')
Use the stripe library. This seemed like the most promising solution but from what I read this is a backend library though I tried to use on the front end. There were no errors with this approach but I figure it hangs on the await
import Stripe from "stripe"
const stripe = new Stripe(process.env.STRIPE_SECRET)
...
const session = await stripe.billingPortal.sessions.create({
customer: 'cus_XXX',
return_url: 'https://example.com/account',
})
console.log(session.url) // Does not reach here
Use a pre-made Stripe link to redirect but the user will have to authenticate on Stripe using their email (this works but I would rather have a short-lived link from Stripe)
<Button component={Link} to={"https://billing.stripe.com/p/login/XXX"}>
Edit payment info on Stripe
</Button>
Using POST HTTPS API call found at https://stripe.com/docs/api/authentication. Unlike the previous options, this optional will register a Stripe Dashboard Log event.
const response = await fetch("https://api.stripe.com/v1/billing_portal/sessions", {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json',
'Authorization': 'bearer sk_test_XXX',
'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *client
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
The error is I'm missing some parameter parameter_missing -customer. So I'm closer to a resolution but I feel as if I should still be able to make the solution above work.
You should use Stripe library to create a billing portal session (your 2nd approach), and you might want to check your Dashboard logs and set the endpoint to /v1/billing_portal/sessions so that you can see if there are any errors during portal session creation.
Given my case, I chose to call the API itself instead of the libraries provided:
export default async function Stripe(payload, stripeEndpoint) {
const _ENDPOINTS = [
"/v1/billing_portal/sessions",
"/v1/customers",
]
let contentTypeHeader = "application/json"
let body = JSON.stringify(payload)
if _ENDPOINTS.includes(stripeEndpoint)) {
contentTypeHeader = "application/x-www-form-urlencoded"
body = Object.keys(payload).map(
entry => entry + "=" + payload[entry]).join("&")
}
try {
// Default options are marked with *
const stripeResponse = await fetch("https://api.stripe.com" + stripeEndpoint, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
headers: {
"Authorization": "bearer " + STRIPE_PRIVATE_KEY,
"Content-Type": contentTypeHeader,
},
redirect: "follow", // manual, *follow, error
referrerPolicy: "no-referrer", // no-referrer, *client
body: body, // body data type must match "Content-Type" header
})
return await stripeResponse.json()
} catch (err) {
console.error(err)
}
}

Fetching resources from google analytics services using HTTPS by Wix fetch function

How should I fetch data using Wix-fetch function?
I followed this google analytics API tutorial, this tutorial using post function for getting JSON data, I used WIX fetch function to get JSON file, but the return object is undefined.
What did I miss?
fetch( "https://accounts.google.com/o/oauth2/token", {
"method": "post",
"headers": {
"Content-Type": 'application/x-www-form-urlencoded'
},
'body' : JSON.stringify({
'grant_type': 'authorization_code',
'code': URLCode,
'client_id': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
'client_secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'redirect_uri': 'https://www.mydomain.or/ga/oauth2callback'
})
} )
.then( (httpResponse) => {
if (httpResponse.ok) {
return httpResponse.json();
} else {
return Promise.reject("Fetch did not succeed");
}
} )
.then( (json) => console.log(json.someKey) )
.catch(err => console.log(err));
UPDATE
STEP 1
I used this URL to generate the CODE
wixLocation.to("https://accounts.google.or/o/oauth2/auth?scope=https://www.googleapis.com/auth/analytics%20https://www.googleapis.com/auth/userinfo.email&redirect_uri=https://www.mydomain.or/ga/oauth2callback/&access_type=offline&response_type=code&client_id=XXXXXXXXXXXXXXXXXX")
I get the CODE from the callback URL
Step 2
I used this code for the HTTP postman request
The redirect URI in step 1 and 2 is the following (the second one):
Step 1:
There needs to be an exact match between the redirect URI configured in the client id in the google developers console and the URL to get the code authorization.
The URL should be built as shown in the tutorial you linked (if you need a refresh token, you can add the access_type=offline)
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/analytics&redirect_uri=<redirect_uri>&response_type=code&client_id=<client_id>
After you enter the URL, you will be provided with an authorization window. Once you authorize, you will be redirected to the <redirect_uri> you provided earlier. You will find the code as the first parameter in the URL query. e.g. <redirect_uri>/?code=<auth_code> ...
Since the access token is for one-time use only, if you will need it again, you will have to get a new <auth_code>.
Step 2 (Postman query example):
If you got the access_token correctly and you want to check now with WIX. Get a new <auth_code> (as said, the access token is given once) and set the code as follows:
import { fetch} from 'wix-fetch';
$w.onReady(function () {
const data = `grant_type=authorization_code&code=<your_authorization_code>&client_id=<your_client_id>&client_secret=<your_client_secret>&redirect_uri=<your_redirect_uri>`;
fetch("https://accounts.google.com/o/oauth2/token", {
"method": "post",
"headers": {
"Content-Type": 'application/x-www-form-urlencoded'
},
'body': data
})
.then((httpResponse) => {
if (httpResponse.ok) {
return httpResponse.json();
} else {
return Promise.reject("Fetch did not succeed");
}
})
.then((json) => console.log(json.access_token))
.catch(err => console.log(err));
});

Get Firebase Access Token in POSTMAN

In my web application, I am using Firebase for Authentication, to access any API, I have to authenticate from firebase.
Question:
How can I get access token of firebase in Postman?
I have 2 solutions for this problem:
1) Get Access Token from firebase in postman, store that access token in postman global env. variable and then I can do other API request. (Here I don't know how to get access token in postman)
2) Do the login in the browser, copy access token from network request, store it in bash_profile and then use it in Postman. (Here I don't know how to read OS env. variable)
When you want to use Postman only and don't want to build a frontend you can use this auth request in Postman: POST https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={API_KEY}
In the Body you should send the following JSON string:
{"email":"{YOUR_EMAIL_ADDRESS}","password":"{PASSWORD}","returnSecureToken":true}
Content type is application/json (will be set automatically in Postman).
You can find the Firebase API_KEY in the Firebase project settings (it's the Web-API-key).
As response you will get a JSON object and the idToken is the token you need for all your API requests as Bearer token.
To have a automated setting of this token, you can add the following code in the Tests tab at your auth request:
var jsonData = JSON.parse(responseBody);
postman.setGlobalVariable("id_token", jsonData.idToken);
For all your API requests you should set the Authorization to Bearer Token and the value for the token is {{id_token}}.
Now the token will be automatically used once you executed the auth request and got the response.
An easy way to retrieve the access token from firebase is to:
create an html file in a directory
copy in the html file the content of firebase auth quickstart
replace the firebase-app.js and firebase-auth.js as explained in firebase web setup to point them at the proper cdn location on the web
replace firebase.init script with the initialization code from your app on the console like this:
var config = {
apiKey: "my secret api key",
authDomain: "myapp.firebaseapp.com",
databaseURL: "https://myapp.firebaseio.com",
projectId: "myapp-bookworm",
storageBucket: "myapp.appspot.com",
messagingSenderId: "xxxxxxxxxxxxx"
};
firebase.initializeApp(config);
open the html file in your browser and either sign in or sign up. The Firebase auth currentUser object value should be displayed.
inspect the html and expand the quickstart-account-details element. This should have the json object displayed.
copy the content of accessToken
In postman go to authorization, select bearer token and paste the copied token in the token value field.
You should be now able to call apis that are secured by firebase auth. Keep in mind that this only gets and passes the access token so once the token is expired you may need to request a new one (steps 5 to 8)
you can also look at this
Hope this helps!
In addition of naptoon's post:
var jsonData = JSON.parse(responseBody);
postman.setGlobalVariable("id_token", jsonData.idToken);
This is "old style", which is deprecated by Postman.
The "new style" is:
pm.environment.set("id_token", pm.response.json().idToken);
go to the pre-request script and add this code (use your API_KEY, USER_EMAIL, USER_PASSWORD)
const reqObject = {
url: "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={API_KEY}", // API_KEY -> your API key from firebase config
method: 'POST',
header: 'Content-Type:application/json',
body: {
mode: 'raw',
raw: JSON.stringify({ "email": {USER_EMAIL}, "password": {USER_PASSWORD}, "returnSecureToken": true })
}
};
pm.sendRequest(reqObject, (err, res) => {
const idToken = res.json().idToken; // your idToken
pm.environment.set("FIREBASE_TOKEN", idToken ); // set environment variable FIREBASE_TOKEN with value idToken
});
this code will add the environment variable FIREBASE_TOKEN, but u can do whatever you want with idToken =)
I came across a need to do this where staging and production environments require a different Firebase idToken but local does not use one. I expanded upon naptoon's and leo's answers to use the identitytoolkit's verifyPassword endpoint as part of a pre-request:
const apiKey = pm.environment.get('api_key');
if ( ! apiKey) {
return
}
const tokenEnv = pm.environment.get('token_env')
if (tokenEnv && tokenEnv === pm.environment.name) {
const tokenTimestamp = Number.parseInt(pm.environment.get('token_timestamp'), 10)
const elapsed = Date.now() - tokenTimestamp
if (elapsed < 20 * 60000) {
return
}
}
pm.sendRequest({
url: `https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=${apiKey}`,
method: 'POST',
header: {
'Content-Type': 'application/json',
},
body: {
mode: 'raw',
raw: JSON.stringify({
email: pm.environment.get('auth_username'),
password: pm.environment.get('auth_password'),
returnSecureToken: true,
}),
},
}, function (err, res) {
let json
if ( ! err) {
json = res.json()
if (json.error) {
err = json.error
}
}
if (err) {
pm.environment.unset('auth_token')
pm.environment.unset('token_env')
pm.environment.unset('token_timestamp')
throw err
}
pm.expect(json.idToken).to.not.be.undefined
pm.environment.set('auth_token', json.idToken)
pm.environment.set('token_env', pm.environment.name)
pm.environment.set('token_timestamp', Date.now())
})
The access token is cached for a given environment for up to 20 minutes (I have not implemented refresh token). The token is cleared if the environment is different to the last request or an error occurs.
Copy the below block of code and place it in the 'pre-request scripts' tab of the request on Postman. It will automatically get a token and put it as 'Authorization' header every time you make a request. You don't need to add any header or authorization manually. You don't even need to worry about token expiry.
Obviously, replace the app api key, username and password place holders.
const postRequest = {
url: 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={APP_API_Key}',
method: 'POST',
header: {
'Content-Type': 'application/json'
},
body: {
mode: 'raw',
raw: JSON.stringify({
"email": "{Your_Email}",
"password": "{Your_Password}",
"returnSecureToken": true
})
}
};
pm.sendRequest(postRequest, (error, response) => {
var jsonData = response.json();
pm.globals.set("id_token", jsonData.idToken)
});
pm.request.headers.add({key: 'Authorization', value: '{{id_token}}'})
Firebase Auth not response Access Token just idToken. you must verify/exchange with your auth system to get it.
Here is the full list apis I found for interacting with Firebase by using its API endpoint directly.
https://www.any-api.com/googleapis_com/identitytoolkit/docs/relyingparty
If your using Node here's my solution,
With the firebase admin SDK import that into your file, and follow #naptoon instructions for setting up a route in PostMan.
In Nodejs in your file put the following
const user = admin.auth().verifyIdToken(req.headers.authorization)
I tried using
const auth = getAuth() const user = auth.currentUser
and that way didn't work for me so I went with the firebase admin route which worked well with minimal code
For anyone still a bit confused, this works perfectly with Firebase using Auth emulators.
Brief Overview
Create functions
Setup emulator
Generate Token
Perform authed request(s)
1. Create functions
2 functions are required:
Generate ID Token function:
import {https} from "firebase-functions";
import {auth} from "firebase-admin";
export const generateAuthToken = https.onCall((data, _context) => {
if (!data.uid) {
return new https.HttpsError("invalid-argument", "Missing UID argument", "Missing UID argument");
}
return auth().createCustomToken(data.uid).then(value => {
console.log(`Token generated: ${value}`);
return {
status: true,
token: value
};
}).catch(reason => {
console.warn(reason);
return {
status: false,
token: ""
}
});
});
(optional) Auth'd function:
import {https} from "firebase-functions";
import {auth} from "firebase-admin";
export const checkAuthenticated = https.onCall((_data, context) => {
if (!context.auth) {
return new https.HttpsError("unauthenticated", "You need to be authenticated to retrieve this data");
}
return "Congratulations! It works.";
});
2. Setup environment
(optional) Setup emulators
Run your firebase project as you'd normally do
Postman, create 2 requests:
1. generateAuthToken
Method: POST
URL: http://127.0.0.1:5001/{project-name}/{region}/generateAuthToken
Headers:
"Content-Type": "application/json; charset=utf-8"
body (RAW: JSON)
{
"data": {
"uid":"1234567890"
}
}
2. checkAuthenticated
Method: POST
URL: http://127.0.0.1:5001/{project-name}/{region}/checkAuthenticated
Headers:
"Content-Type": "application/json; charset=utf-8"
body (RAW: JSON)
{
"data": {
}
}
Authentication Tab > Type Bearer: {insert token}
3. Generate Token
Call postman function using method described in 2.1
4. Perform authed request(s)
For every authed request, add the bearer token as described in 2.2 and it all works as expected.

Network request failed on Android when using fetch to post to cloud functions

I am using the guide explained here
to upload images to firebase cloud storage using cloud function as a middle ware or at least that what I understood from the guide.
My issue is with the fetch request at the client side:
const body = new FormData();
body.append("picture", {
uri: imageUri,
name,
type: type
});
await fetch(`${cloudFunctionUrl}`, {
method: "POST",
body,
headers: {
Accept: "application/json",
"Content-Type": "multipart/form-data"
}
}).then(async() => {
// do things
}).catch(error => {
console.log(error);
reject(error);
});
On iOS it works like a charm but on Android I get a network request failed error.
The publisher of the guide above declared that the method works for both iOS and Android.
Some topics suggested that the problem is with the headers of the request when it comes to Android, but didn't specify exactly what is wrong.
The issue was passing type = "jpg" while it should be type= "image/jpg". Thanks to Kadi

"415 Error" when querying Spotify for tokens

I've been trying to recreate the spotify oauth connection in MeteorJS. I've gotten as far as requesting the access and refresh tokens, but I keep getting a 415 error now. Here is the relevant code:
var results = HTTP.post(
'https://accounts.spotify.com/api/token',
{
data: {
code: code,
redirect_uri: redirectURI,
grant_type: 'authorization_code',
client_id: clientID,
client_secret: clientSecret
},
headers: {
'Content-Type':'application/json'
}
}
);
I can't seem to find any other good documentation of the problem and the code in this demo:
https://github.com/spotify/web-api-auth-examples/tree/master/authorization_code
works perfectly.
I had a similar problem (but in Java). The analogous solution was
headers: {
'Content-Type':'application/x-www-form-urlencoded'
}
You need to use params instead of data when sending the JSON object. Related question: Unsupported grant type error when requesting access_token on Spotify API with Meteor HTTP
I have successfully tried getting the access token from Spotify, using the below function. As you can see, you don't need to specify Content-Type, but just need to use params instead of data (as far as axios is concerned). Also make sure that you first combine the client id and the client secret key with a ":" in between them and then convert the combined string into base 64.
let getAccessToken = () => {
let options = {
url: 'https://accounts.spotify.com/api/token',
method: 'POST',
headers: {
// 'Content-Type':'application/x-www-form-urlencoded',
'Authorization': `Basic <base64 encoded client_id:client_secret>`
},
params: {
grant_type: 'client_credentials'
}
}
axios(options)
.then((resp) => {
console.log('resp', resp.data)
})
.catch((err) => {
console.log('ERR GETTING SPOTIFY ACCESS TOKEN', err);
})
}
If youre doing this clientside its not working because you're not allowed to post to another domain from the client side because of the same origin policy.
If this is server-side I'd recommend using a pre-existing spotify api npm module instead of writing your own requests. There are plenty of spotify api implementations on npmjs.org.
Use arunoda's npm package for integrating npm packages in your meteor application

Resources