I created an auth service from scratch using Redux, React and Node. Everything was working fine until I wire up my Post section o redux to my BackEnd. The redux part is ok I guess. My problem is when I send the Authorization Bearer token. I'm being able to post using insomnia. But when I try to post using the web app I can't.
This is my action:
export const createPost = ( formValues: any) => async(dispatch: any, getState: any) => {
const { userId } = getState().auth;
let token = userId
const headers = {
header: {
'Content-Type' : 'application/json',
'Accept' : 'application/json',
Authorization: `Bearer ${token}`
}
};
const response = await AlleSys.post('/posts', {...formValues, headers})
// dispatch({type: CREATE_POST, payload: response.data})
userId is my JWT token.
I already set up Cors on my backend
const corsOptions ={
origin:'http://localhost:3000',
credentials:true, //access-control-allow-credentials:true
optionSuccessStatus:200
}
app.use(cors(corsOptions))
On Insomnia. The same request on insomnia works fine.
On insomnia I'm using the same bearer token from my application, so the problem is not the JWT.
Querying an endpoint with GET, POST, PUT, DELETE from a Nodejs server or Insomnia will result in calling before checking the OPTIONS.
But browsers will limit the HTTP requests to be at the same domain which makes you run into CORS issues. Since Insomnia is not a browser and CORS is a browser security restriction only, it didn't get limited.
From docs for the CORS you are using:
Certain CORS requests are considered 'complex' and require an initial OPTIONS request (called the "pre-flight request"). An example of a 'complex' CORS request is one that uses an HTTP verb other than GET/HEAD/POST (such as DELETE) or that uses custom headers. To enable pre-flighting, you must add a new OPTIONS handler for the route you want to support:
So I think you should include app.options('*', cors()) before all routes and put it at the top of your file to be processed first.
I changed my code to:
export const createPost = ( formValues: any) => async(dispatch: any, getState: any) => {
const { userId } = getState().auth;
let token = userId
const headers = {
authorization: `Bearer ${token}`
};
const response = await AlleSys.post('/posts', {...formValues}, {headers})
And Worked!
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 :)
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.
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
I am new to mobile development. My project is build using asp.net. For authentication I am using build it UserManager & User.Identity.
I have bunch of existing web apis and I wish to use them from mobile app.
I know , I could pass a secret hash to web api after authenticating, but that would involve a huge code refactoring.
I been wondering if there other ways to handle authentication & authorization with nativescript & asp.net .
Do you know any useful resources for this topic?
Many thanks for your help!
It depends quite heavily on your API structure, but I would recommend somethign like this:
Firstly you would need to use the Nativescript Http module. An implementation to get a an HTTP GET calls returned header might look like this:
http.request({ url: "https://httpbin.org/get", method: "GET" }).then(function (response) {
//// Argument (response) is HttpResponse!
//for (var header in response.headers) {
// console.log(header + ":" + response.headers[header]);
//}
}, function (e) {
//// Argument (e) is Error!
});
So your backend might return a JSON Web Token as a header. In which case on the success callback you would probably want to store your token in the applications persistent memory. I would use the Application Settings module, which would look something like:
var appSettings = require("application-settings");
appSettings.setString("storedToken", tokenValue);
Then before you make an API call for a new token you can check if there is a stored value:
var tokenValue = appSettings.getString("storedToken");
if (tokenValue === undefined {
//do API call
}
Then with your token, you would want to make an API call, e.g. this POST and add the token as a header:
http.request({
url: "https://httpbin.org/post",
method: "POST",
headers: { "Content-Type": "application/json", "Auth": tokenValue },
content: JSON.stringify({ MyVariableOne: "ValueOne", MyVariableTwo: "ValueTwo" })
}).then(function (response) {
// result = response.content.toJSON();
// console.log(result);
}, function (e) {
// console.log("Error occurred " + e);
});
Your backend would need to check the Auth header and validate the JWT to decide whether to accept or reject the call.
Alternatively, there some nice plugins for various Backends-as-a-Service, e.g. Azure and Firebase