Goal
Using googleapis with firebase functions. Get a JWT token so firebase functions can use a service account with domain-wide delegation to authorize G Suite APIs like directory and drive.
Question
What goes in path.join();
What is __dirname
What is 'jwt.keys.json'?
From this example:
https://github.com/googleapis/google-api-nodejs-client/blob/master/samples/jwt.js
// Create a new JWT client using the key file downloaded from the Google Developer Console
const auth = new google.auth.GoogleAuth({
keyFile: path.join(__dirname, 'jwt.keys.json'), // <---- WHAT GOES IN path.join()
scopes: 'https://www.googleapis.com/auth/drive.readonly',
});
Error
When I run
const auth = new google.auth.GoogleAuth({
keyFile: path.join(__dirname, "TEST"), // <-- __dirname == /srv/ at runtime
scopes: 'https://www.googleapis.com/auth/drive.readonly',
});
From the GCP Logs I get this error:
Error: ENOENT: no such file or directory, open '/srv/TEST'
Obviously TEST isn't valid, but is '/srv/?
What is the keyFile, a file path? a credential?
Another Example
https://github.com/googleapis/google-api-nodejs-client#service-to-service-authentication
I found documentation here:
https://googleapis.dev/nodejs/google-auth-library/5.10.1/classes/JWT.html
If you do not want to include a file, you can use key, keyId, and email to submit credentials when requesting authorization.
You seem to have a lot of questions around how this works. I would strongly encourage you to read the basics of Google authentication.
JWT is short for JSON Web Token. It is a standard standard defining secure way to transmit information between parties in JSON format. In your code "jwt" is a class containing a keys property. There are a ton of JWT libraries. There are some popularly packages using Node/Express frameworks.
__dirname // In Node this is the absolute path of the directory containing the currently executing file.
path.join is a method that joins different path segments into one path.
Here you are taking the absolute path and concatenating some piece of information to the end of the path. I am not certain what is contained in jwt.keys.json but that is what is being appended to the end of the absolute path in this case.
Without knowing your project structure or what you are pointing to it's not really possible to say what is and is not a valid path in your project.
keyFile is a key in an object (as denoted by the {key: value} format) under google.auth. As seen in the sample code you referenced, the script is taking the google.auth library and calling a method to construct and object with the information to are providing so that it abstract away other elements of the authentication process for you. You are giving it two pieces of information: 1) The location of the keyFile which presumably are the credentials and 2) The scope or set of permissions you are allowing. In the example it is readonly access to Drive.
EDIT: The private key file that the calling service uses to sign the JWT.
Related
I want to use Google Drive API to manage files programaticaly.
To do that, I use ID clients OAuth 2.0 Web Application. In OAuth consent screen (Edit App), I check .../auth/drive and .../auth/drive.readonly scopes.
I'm log in, I have token.json file. I can list and download files
Now, when I want to delete file, I do this in python : service.files().delete(fileId=item['id']).execute(). But I have this issue :
An error occurred: <HttpError 403 when requesting https://www.googleapis.com/drive/v3/files/<fileID>? returned "The user has not granted the app <appID> write access to the file <fileID>.". Details: "[{'message': 'The user has not granted the app <appID> write access to the file <fileID>.', 'domain': 'global', 'reason': 'appNotAuthorizedToFile', 'location': 'Authorization', 'locationType': 'header'}]">
What I do wrong ?
Thanks in advance !
The scopes declared in Google cloud console are mainly for use with verification of your application. Its your code that defines what scopes your application is requesting of the user.
I am going to assume that you are following the Quick start for python or quick start for node.js both use token.json for credential storage. The answer is the same either way.
This sample shows you how to use the files.list method which allows for a readonly scope.
While the file.delete does not allow for a readonly scope it requires write access.
Fix
In the code you can see that the scope is readonly
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
This means that when the user authorized your app they authorized it with a readonly scope giving you access to read the files only. This the users credentials are now stored in token.json
To fix your issue change the scope to
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
Then delete the token.json file and your app should now be resting full drive access giving you the ability to make changes like delete the files.
According to the Vue docs, I can store environment variables in .env files and they are loaded automatically. This works fine but this page says to not store any sensitive ENV variables in these files since they are bundled with the build.
WARNING
Do not store any secrets (such as private API keys) in your app!
Environment variables are embedded into the build, meaning anyone can
view them by inspecting your app's files.
https://cli.vuejs.org/guide/mode-and-env.html#environment-variables
So, if I am developing a webapp that talks to an API that requires an ID and a secret to be sent in order to get a token, where am I supposed to store these ENV variables (CLIENT_ID, CLIENT_SECRET)?
Locally I have an .env.local file that contains these values and I'm also defining them in the Netlify env variables.
Anything secret should be stored only on your server. It should never be part of your Vue app source code no matter how well you think you've hidden it, or encrypted it.
Generally speaking, you should have the user send their username/password, create a token on the server (look into JSON Web Tokens for a lightweight approach) and then either (1) Set it as an HttpOnly cookie (one that javascript cannot touch) or store it in localStorage on the client and set it as a common header in axios (using headers: { Authorization: 'Bearer ' } is fairly common.
Now, the client should be sending back that JWT with every request. So on your server, you can check the JWT is valid, and if so, send back the data requested.
I am using Vercel Deployments with a NextJS app. Deployments are automatically run when I push to master, but I don't want to store keys in GitHub. My serverless functions are reliant on my database. When run locally, I can simply use Google's Default Authentication Credentials, but this is not possible when deployed to Vercel. As such, I created a Service Account to enable the server to have access.
How do I load the service account credentials without pushing they key itself to GitHub?
I tried adding the key as described in this issue, but that didn't work.
AFAIK setting an environment variable in Vercel is not helpful because Google environment variables require a path/JSON file (vs. simply text).
Rather than using a path to a JSON file, you can create an object and include environment variables as the values for each object key. For example:
admin.initializeApp({
credential: admin.credential.cert({
client_email: process.env.FIREBASE_CLIENT_EMAIL,
private_key: process.env.FIREBASE_PRIVATE_KEY,
project_id: 'my-project'
}),
databaseURL: 'https://my-project.firebaseio.com'
});
Then, you can add the environment variables inside your project settings in Vercel.
Adding to #leerob's answer, I found that putting quotes around the FIREBASE_PRIVATE_KEY environment variable in my .env file fixed an error I kept getting relating to the PEM file when making a request. I didn't need any quotes around the key for calls to the standard firebase library though.
This was the config I used to access the Google Cloud Storage API from my app:
const { Storage } = require('#google-cloud/storage');
const storage = new Storage({ projectId: process.env.FIREBASE_PROJECT_ID,
credentials: { client_email: process.env.FIREBASE_CLIENT_EMAIL,
private_key: process.env.FIREBASE_PRIVATE_KEY_WITH_QUOTES
}
})
I had this problem too but with google-auth-library. Most of Googles libraries provide a way to add credentials through a options object that you pass when initializing it. To be able to get information from Google Sheets or Google Forms you can for example do this:
const auth = new GoogleAuth({
credentials:{
client_id: process.env.GOOGLE_CLIENT_ID,
client_email: process.env.GOOGLE_CLIENT_EMAIL,
project_id: process.env.GOOGLE_PROJECT_ID,
private_key: process.env.GOOGLE_PRIVATE_KEY
},
scopes: [
'https://www.googleapis.com/auth/someScopeHere',
'https://www.googleapis.com/auth/someOtherScopeHere'
]
});
You can just copy the info from your credentials.json file to the corresponding environment variables. Just take care that when your working on localhost you will need to have the private_key in double quotes but when you put it into Vercel you should not include the quotes.
I have a JavaScript file that needs to use the GitHub API to get the contents of a file stored in a repository.
I am using GitHub.js to access the API, and it shows the following method for authentication:
// basic auth
var gh = new GitHub({
username: 'FOO',
password: 'NotFoo'
/* also acceptable:
token: 'MY_OAUTH_TOKEN'
*/
});
This code will be viewable inside the repository, as well as in the developer settings in the browser. GitHub does not allow you to commit a file that contains an OAuth token, and publicly displaying the username and password for my account is obviously a non-option as well.
Is there any other way to do authentication? I tried using the client-id and client-secret but it doesn't seem to take those as valid credentials.
Try an Installation Access Token. I can't remember the specifics but I used that link to set myself up.
I just tried to test wolkenkit’s authentication with the chat template following the wolkenkit docs. User login seems to work, but the user is redirected to Auth0 even when they're already logged in (without the client calling the auth.login method).
Here’s a code snippet from the client:
wolkenkit.connect({
host: 'local.wolkenkit.io',
port: 3000,
authentication: new wolkenkit.authentication.OpenIdConnect({
identityProviderUrl: 'https://<myIdentity>.eu.auth0.com/authorize',
clientId: '<myClientID>',
strictMode: false
})
}).
then(chat => {
console.log("chat.auth.isLoggedIn() = " + chat.auth.isLoggedIn());
console.log(chat.auth.getProfile());
if (!chat.auth.isLoggedIn()) {
return chat.auth.login();
}
});
In package.json, the identity provider is configured as followed:
"wolkenkit": {
"environments": {
"default": {
"identityProvider": {
"name": "https://<myIdentity>.eu.auth0.com/",
"certificate": "/server/keys/<myIdentity>.eu.auth0.com"
},...
Browser log after clearing cookies (I censored the provider identity and the object returned by chat.auth.getProfile()):
Navigated to http://local.wolkenkit.io:8080/
index.js:14 chat.auth.isLoggedIn() = false
index.js:15 undefined
Navigated to https://<myIdentity>.eu.auth0.com/login?client=<clientID>...
Navigated to http://local.wolkenkit.io:8080/
index.js:14 chat.auth.isLoggedIn() = true
index.js:15 {iss: "https://<myIdentity>.eu.auth0.com/", sub: "auth0|...", aud: "...", iat: ..., exp: ..., …}
Navigated to https://<myIdentity>.eu.auth0.com/login?client=<clientID>...
Being redirected although you configured authentication typically means that there is an error in the way the authentication is configured.
You might want to check these settings:
The token must be signed using RS256, not HS256 (which, for some accounts, seems to be the default of Auth0). To find out which signature algorithm is being used, get the token from the browser's local storage and paste it into the JWT debugger. Then you can see how the token was signed. If you need to change the signature algorithm, you can find this in the Advanced Settings of your client in Auth0.
Using the very same debugger you can also verify whether the token and the certificate you are using match each other. If they don't, you probably have copied the wrong certificate, or you have configured the path to point to a wrong certificate.
The certificate file must be named certificate.pem. If it has another name, or the path in package.json is incorrect, wolkenkit should not even start the application, but to be sure double-check that the file is named correctly.
In the package.json, besides the path to the certificate, you also have to provide the name of the identity provider you use, in your case this is https://<myIdentity>.eu.auth0.com/. Please note that this must exactly match the iss claim within the token. Often the claim in the token contains a trailing slash, while the value in package.json does not. If they differ, use the token's value in package.json.
Once you have made your changes, make sure to empty local storage, and to restart your wolkenkit application using the following command (otherwise your changes won't become effective):
$ wolkenkit restart
Hope this helps :-)