const { data: geoLocationData } = useSWR(
!acceptedLocation ? 'https://geolite.info/geoip/v2.1/country/me' : null,
fetchWithToken,
{
shouldRetryOnError: false
}
);
Cors Policy error doesn't allow me to request from GeoLite2 API
According to their documentation, the only thing needed is the Authentication header, already set up.
Related
The functions I have created is working fine in local using firebase serve command. When I deployed it to firebase functions it started to throw error:
"Access to XMLHttpRequest at 'https://us-central1-mysample.cloudfunctions.net/api/configSettings' from origin 'https://mysample.web.app' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource"
Below are the key points about the project & code:
Using ExpressJs for web API creation
Both Hosting and Functions are inside same Firebase project only
I have tried below CORS npm package implementation
const cors = require('cors')
var corsOptionsDelegate = function (req, callback) {
console.log("req.header('Origin') : ", req.header('Origin'))
var corsOptions;
if (whitelist.indexOf(req.header('Origin')) !== -1) {
corsOptions = { origin: true } // reflect (enable) the requested origin in the CORS
response
} else {
corsOptions = { origin: false } // disable CORS for this request
}
callback(null, corsOptions) // callback expects two parameters: error and options
}
app.options('*', cors(corsOptionsDelegate));
Also I have tried with below way:
app.use((req, res, next) => {
const allowedOrigins = ['https://mysample.web.app', 'https://mysample.firebase.webapp'];
const origin = req.headers.origin;
console.log("origin : ", origin)
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Credentials', true);
return next();
});
But none of the way resolved my issue. Is there any setting I have to in Firebase portal ? Any help.
#Vaira Selvam RajaGopalan confirmed the issue was solved by adding a new role Cloud Functions Invoker to "allUsers" .To invoke a cloud function, a user must be assigned the Cloud Functions Invoker role.
To allow unauthenticated invocation of a function, grant the Cloud Functions Invoker role to the special allUsers principal on the function:Refer this document
Go to the Google Cloud console:
Click the checkbox next to the function to which you want to grant access.
Click Permissions at the top of the screen. The Permissions panel opens.
Click Add principal.
In the New principals field, type allUsers.
Select the role Cloud Functions > Cloud Functions Invoker from the Select a role drop-down menu.
Click Save.
I have 3 projects:
Client App
ASP.NET API App
IdentityServer4 MVC App
I am able to send a request from API to IDP but trying to send a request from Client to IDP yields
"CORS request made for path: /api/Trial/TrialAction from origin: https://localhost:44389 but
was ignored because path was not for an allowed IdentityServer CORS endpoint"
even though I added the following to the IDP:
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", policyBuilder => policyBuilder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
and
// ...
app.UseRouting();
app.UseIdentityServer();
app.UseCors("CorsPolicy");
app.UseAuthorization();
// ...
The interesting part is, I can send a request from API to IDP without adding CORS configuration to IDP. What am I doing wrong?
Config.cs:
public static class Config
{
public static IEnumerable<IdentityResource> Ids =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
};
public static IEnumerable<ApiResource> Apis =>
new ApiResource[]
{
new ApiResource("myapi",
"My API",
new [] { "membershipType" }
)
};
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "mywebclient",
ClientName = "My Web Client",
AllowedGrantTypes = GrantTypes.Code, // Authorization code flow with PKCE protection
RequireClientSecret = false, // Without client secret
RequirePkce = true,
RedirectUris = { "https://localhost:44389/authentication/login-callback" },
PostLogoutRedirectUris = { "https://localhost:44389/authentication/logout-callback" },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"albidersapi"
},
AllowedCorsOrigins = { "https://localhost:44389" },
RequireConsent = false,
}
};
}
do yo have the client and API in the same project as IdentityServer? I typically recommend that you keep them apart.
A wild guess could be to swap these two lines:
app.UseIdentityServer();
app.UseCors("CorsPolicy");
Because apparently IdentityServer captures the request to the API?
The most likely issue is that your call from your client to your API is not including the access token.
The debug log is coming from this file here. If you look at where your debug statement is originating from you will see that it is checking if the path matches any within IdentityServerOptions.Cors.CorsPaths. Here is an image of what those paths generally are from a debug service I made.
These paths are just the default information and authentication endpoints for IdentityServer4. In other words it thinks your request is unauthenticated because it likely isn't including the access token.
If you are using IdentityServer4's template logging implementation with Serilog, then you can also add this to your appsettings.json to see what the ASP.NET Core CORS middleware has to say. It will be logging after IdentityServer4's log
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Authentication": "Debug",
"Microsoft.AspNetCore.Cors": "Information",
"System": "Warning"
}
}
}
Here is what my debug log looked like when I made a request to an endpoint with a proper CORS policy, but the request didn't include its access token.
[21:05:47 Debug] IdentityServer.Hosting.CorsPolicyProvider CORS request made for path: /api/v1.0/users/{guid}/organizations from origin: https://localhost:44459 but was ignored because path was not for an allowed IdentityServer CORS endpoint
[21:05:47 Information] Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware No CORS policy found for the specified request.
So it's not a CORS issue really. It's an access token or authentication issue. It is also possible, however, that your endpoint isn't being hit properly. However, you should be receiving a 404 on the client in addition to the log seen above.
I am building an app for the web hosted on Firebase where I am trying to allow unauthenticated users to upload an image to Storage (after passing a reCAPTCHA).
To do this I have a React website and a Firebase function that validates the reCAPTCHA and then generates a signed URL for the client-side code to upload the image to the default bucket. I have deployed the Function to the cloud because there is no Storage emulator I can use for local development but my React app is being served locally.
The code below omits the usual boilerplate.
Firebase Function Code:
const id = uuidv4()
const bucket = storage.bucket("project-id.appspot.com");
const file = bucket.file(id)
const expires_at = Date.now() + 300000;
const config = {
action: 'write',
version: 'v2',
expires: expires_at,
contentType: 'application/octet-stream'
};
file.getSignedUrl(config, (err, url) => {
if (err) {
console.error(err);
res.status(500).end();
return;
}
res.send(url);
});
This returns a URL to the client of the form: https://storage.googleapis.com/project-id.appspot.com/db9a5cc5-6540-4f40-933f-cfdb287b15a9?GoogleAccessId=project-id%40appspot.gserviceaccount.com&Expires=1594719835&Signature=<signature here>
Client-side Code:
Once this is received on the client-side I try to upload the file to that URL using the PUT method:
// signed_url is the url returned from the first API call to the function above.
// image_file is the file data I get from using react-dropzone.
axios({
method: 'put',
url: signed_url,
data: image_file,
headers: {'Content-Type':'application/octet-stream'}
}).then(res => {
console.log("success");
console.log(res);
}).catch((error) => {
console.log(error);
});
This is where I get a CORS error of the form: Access to XMLHttpRequest at 'https://storage.googleapis.com/project-id.appspot.com/db9a5cc5-6540-4f40-933f-cfdb287b15a9?GoogleAccessId=project-id%40appspot.gserviceaccount.com&Expires=1594719835&Signature=<signature here>' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I used cURL to see if the CORS headers in the response were being set and they were not. I then used gsutil cors set config.json gs://project-id.appspot.com to set the CORS permissions on the default bucket. Here is the format of the configuration:
[
{
"origin": ["*"],
"responseHeader": [
"Origin",
"Accept",
"x-goog-resumable",
"Content-Type",
"Access-Control-Allow-Origin",
"Access-Control-Allow-Headers",
"Authorization",
"X-Requested-With"
],
"method": ["GET, POST, PUT, PATCH, POST, DELETE, OPTIONS"],
"maxAgeSeconds": 3600
}
]
I checked the service account to make sure they had the Service Account Token Creator permission and Storage Object Creator permissions set and they did.
I followed the steps at https://cloud.google.com/functions/docs/writing/http#gcloud and I have tried every combination of content-type headers and tried v2 and v4 of getSignedURL version, as well as following any other suggestions I could find online, but to no avail.
I have the following codes that verify Google reCAPTCHA v3 in my Firebase Function that caused the CORS issue:
const functions = require('firebase-functions');
const nodemailer = require("nodemailer");
const express = require("express");
const cors = require("cors");
const request = require('request');
const serverApi = express();
api.use(cors({ origin: true }));
function verifyCaptcha(token, returnData) {
// Put your secret key here.
var secretKey = functions.config().recaptcha.secretkey;
var verificationUrl = "https://www.google.com/recaptcha/api/siteverify?secret=" + secretKey + "&response=" + token;
// Note here: External network call to google.com
request(verificationUrl, function (error, response, body) {
body = JSON.parse(body);
// Success will be true or false depending upon captcha validation.
if (!body.success) {
body['status'] = false;
body['errSource'] = "recaptcha";
body['message'] = "Failed to pass captcha verification.";
} else {
body['status'] = true;
body['message'] = "Successfully passed captcha verification!";
};
console.log(`Google returns: ${JSON.stringify(body)}`);
returnData(body);
});
};
api.post("/api/service-name", (req, res) => {
if (!req.body['g-recaptcha-response']) {
return res.send({ "status": false, "errSource": "recaptcha", "message": "Client-side reCAPTCHA token not found." });
};
const recaptchaToken = req.body['g-recaptcha-response'];
verifyCaptcha(recaptchaToken, function (result) {
if (result.status == false) {
return res.send(result);
};
// My business logics here.
});
});
exports.api = functions.https.onRequest(api);
I noticed that after removing the reCAPTCHA v3 verification request in within my Firebase Function, no more CORS issue for my localhost to call "/api/service-name" using $.ajax(). This is because the following Firebase Function log reminded me of the "External network is not accessible":
Billing account not configured. External network is not accessible and quotas are severely limited.
Configure billing account to remove these restrictions
My question is: Is there a way to get my server-side reCAPTCHA verification to work without causing this CORS issue, which could be prevented by "Billing account not configured"? Thanks!
UPDATE:
After catching the request() error that does the verification, I get the following error:
{errno: "EAI_AGAIN", code: "EAI_AGAIN", syscall: "getaddrinfo", hostname: "www.google.com", host: "www.google.com", …}
Also, after handling this error, no more CORS issue, but reCAPTCHA still cannot be verified. Any idea what causes this? Thanks again!
It's now confirmed that the above issue has been resolved after Enable Billing at the Google Cloud Console. It is NOT actually the CORS issue between the localhost and Firebase Functions/Hosting (although the Chrome browser returned as CORS related error message), it's actually the HTTP Request from the Firebase Function to the Google reCAPTCHA api during token verification process. Due to billing account not linked to the Firebase Project where the function sits in, any requests from any Firebase Functions to any External Network Resources, including Google reCAPTCHA, will be rejected with the following errors:
HTTP Request Error:
{errno: "EAI_AGAIN", code: "EAI_AGAIN", syscall: "getaddrinfo", hostname: "www.google.com", host: "www.google.com", …}
After enabling billing at GCP and linking the billing account to the specific Firebase Project, the request to Google reCAPTCHA verification will be successful (if the token is valid) without the above error. However, your FREE Spark Tier Firebase account will be AUTOMATICALLY UPGRADED to Blaze Plan -- Pay as you go.
I have an ASP.NET SPA with a adal-js based authentication, and an ASP.NET Web Api website with Azure Active Directory auth
Both websites are hosted on Azure, on different hostnames, say
https://foo.azurewebsites.com/ and https://fooapi.azurewebsites.com/
The Web Api website auth is configured as
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters() { ValidAudience = ConfigurationManager.AppSettings["ida:Audience"] },
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
});
}
}
and Main SPA adal.js is initialized as:
var config = {
instance: "https://login.microsoftonline.com/",
tenant: "mytenant",
clientId: "client id of foo registration",
postLogoutRedirectUri: "https://foo.azurewebsites.com/",
cacheLocation: "localStorage"
};
authContext = new AuthenticationContext(config);
// Check For & Handle Redirect From AAD After Login
var isCallback = authContext.isCallback(window.location.hash);
authContext.handleWindowCallback();
var errorMessage = authContext.getLoginError();
if (isCallback && !authContext.getLoginError()) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
// Check if View Requires Authentication
if (!authContext.getCachedUser()) {
authContext.config.redirectUri = window.location.href;
authContext.login();
return;
}
The Tenant is the same for foo and fooapi, the client id is different (one for each app registration).
The authentication flow in the foo web app is performed successfully, but every http request to fooapi returns 401 unauthorized.
How can I make fooapi share the successful authentication of foo ?
Thank you for any hint
You can use the implicit grant flow in AAD so that an ID Token is received and sent in auth header when API call is made. See below links for the details and sample code.
https://azure.microsoft.com/en-gb/documentation/articles/active-directory-authentication-scenarios/#single-page-application-spa
https://github.com/Azure-Samples/active-directory-angularjs-singlepageapp
How you acquire the access token for the web API?
To make sure the request successfully, you need to acquire the token using the resource you config in web API. You can pass the token from here to check whether the aud claim is equal to the value ida:Audience.
And also make sure the token is issued from the tenant you config in web API project since you didn't ignore the tenant verification.
Please configure your web point into endpoints and add it to initialization.
var endpoints = {`enter code here`
"https://yourhost/api": "b6a68585-5287-45b2-ba82-383ba1f60932",
};
adalAuthenticationServiceProvider.init(
{
// Config to specify endpoints and similar for your app
tenant: "52d4b072-9470-49fb-8721-bc3a1c9912a1", // Optional by default, it sends common
clientId: "e9a5a8b6-8af7-4719-9821-0deef255f68e", // Required
//localLoginUrl: "/login", // optional
//redirectUri : "your site", optional
endpoints: endpoints // If you need to send CORS api requests.
},
$httpProvider // pass http provider to inject request interceptor to attach tokens
);