How to instantiate Octokit for user-as-a-server oAuth? - github-api

I want my GitHub app to perform actions on behalf of a user, like creating an issue.
I am authenticating my user this way:
const { createOAuthAppAuth } = require('#octokit/auth-oauth-app');
const auth = createOAuthAppAuth({
clientType: 'oauth-app',
clientId: clientGithubID,
clientSecret: clientSecretGithub,
});
const userAuthenticationFromWebFlow = await auth({
type: 'oauth-user',
code,
state: userId,
});
// console.log(useAuthenticationFromWebFlow)
// {
// "type": "token",
// "tokenType": "oauth",
// "clientType": "oauth-app",
// "clientId": "Iv1.CLIENTID",
// "clientSecret": "CLIENTSECRET",
// "token": "ghu_bunchofchars",
// "scopes": []
// }
It seems that the token that I get is good.
Now the problem is when I try to create an Octokit later in my code this doesn't work
octokit = new Octokit({
authStrategy: userAuthenticationFromWebFlow,
});
await octokit.issues.createComment({...props})
What is the proper way to create an Octokit instance ?
Thank you !

So I just found the answer,
may be that can help someone.
const { createOAuthAppAuth } = require('#octokit/auth-oauth-app');
octokit = new Octokit({
authStrategy: createOAuthAppAuth,
auth: userAuthenticationFromWebFlow.token,
});
And it works !

Related

how to create a postgresql instance using OAuth2Client in google using nodejs client library

i have a requirement for creating PostgreSQL instance using google SQL admin API, for the authentication, i want to use OAuth2Client in node js client library
function first() {
const oAuth2Client = new google.auth.OAuth2(
client_id,
client_secret,
redirect_uris[0]
);
var tkn = await oAuth2Client.getToken(code_from_another_user);
oAuth2Client.setCredentials(tkn);
return oAuth2Client;
});
function second(oAuth2Client)
{
var req = {
project: prjName,
resource: {
databaseVersion: "POSTGRES_13",
name: name,
region: cregion ,
gceZone: czone,
settings: {*****}
rootPassword: "xxxxx",
},
auth: oAuth2Client,
};
var crpsql = await sqlAdmin.instances.insert(req);
return crpsql;
});
function mainexec()
{
var a = first();
var b = second(a);
});
and i get this error
Error: No access, refresh token, API key or refresh handler callback
is set
here actually, i am trying to create a PostgreSQL instance on other google account cloud platform with there consent using OAuth2Client access token method. anyone please help? any supporting documentation?.
The function first returns oAuth2Client as it is . But in the function second it is converted to JSON object automatically.
so changed the function named second like this
function second(oAuth2Client)
{
var newoAuth2Client = new google.auth.OAuth2(
oAuth2Client._clientId,
oAuth2Client._clientSecret,
oAuth2Client.redirectUri
);
var tokenObj = {
access_token: oAuth2Client.credentials.tokens.access_token,
refresh_token: oAuth2Client.credentials.tokens.refresh_token,
};
newoAuth2Client.credentials = tokenObj;
var req = {
project: prjName,
resource: {
databaseVersion: "POSTGRES_13",
name: name,
region: cregion ,
gceZone: czone,
settings: {*****}
rootPassword: "xxxxx",
},
auth: newoAuth2Client,
};
var crpsql = await sqlAdmin.instances.insert(req);
return crpsql;
});
it worked like a magic.

No refreshToken in msal-node when providing scope "offline-access"

I hope you can help me: I currently develop an app which needs access to the users calendar (outlook-calendar) to find free meeting slots (other users will be able to see and then select one of the free slots - similar to calendly). For that I use msal-node to authenticate against azureAD. But my use case needs "everytime"-access to the calendars from all users. This is why I want to get an refresh_token. The docs of msal-node say that I should provide the offline_scope to get an refreshtoken while doing the OAuth-process.
My problem is that I receive an access_token and id_token and so on, but no refreshtoken. The Azure-response further shows a successful answer but when I take a look into the returned scopes I cannot find offline_scope.
You can see the returned scopes here
What should I do?
I use a cofidentalClientApplication msal-node instance:
const oauth2Client = MicrosoftClient.Connection
const authCodeUrlParameters = {
scopes: ["offline_access", "user.read"],
forceRefresh: true,
redirectUri: "http://localhost:3000/outlookRedirect",
}
try {
console.log("GDFHGJF")
return oauth2Client.getAuthCodeUrl(authCodeUrlParameters)
}
After receiving the code from Azure, I process it via:
const oauth2Client = MicrosoftClient.Connection
const tokenRequest = {
code: code,
scopes: ["user.read", "offline_access"],
forceRefresh: true,
redirectUri: "http://localhost:3000/outlookRedirect",
//client_secret: process.env.MICROSOFTCLIENTSECRET,
}
const testus = await oauth2Client.acquireTokenByCode(tokenRequest)
const tokenRequest2 = {
scopes: ["user.read", "offline_access"],
forceRefresh: true,
redirectUri: "http://localhost:3000/outlookRedirect",
account: testus.account,
}
oauth2Client
.acquireTokenSilent(tokenRequest2)
.then((response) => {
console.log("\nResponse: \n:", response)
})
.catch((error) => {
console.log(error)
})
return
What is my fault? I appreciate any kind of help!
Thank you in advance,
Lukas
after calling 'acquireTokenByCode' , 'pca' now has the refresh token. const tokenCache = pca.getTokenCache().serialize(); const refreshTokenObject = (JSON.parse(tokenCache)).RefreshToken const refreshToken = refreshTokenObject[Object.keys(refreshTokenObject)[0]].secret;
Below is a complete snippet of How to get the Refresh and Access token.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
const express = require("express");
const msal = require('#azure/msal-node');
const SERVER_PORT = process.env.PORT || 3000;
const REDIRECT_URI = "http://localhost:3000/redirect";
// Before running the sample, you will need to replace the values in the config,
// including the clientSecret
const config = {
auth: {
clientId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
authority: "https://login.microsoftonline.com/84fb56d3-e15d-4ae1-acd7-cbf83c4c0af3",
clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
},
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
         piiLoggingEnabled: false,
         logLevel: msal.LogLevel.Verbose,
        }
    }
};
// Create msal application object
const pca = new msal.ConfidentialClientApplication(config);
// Create Express App and Routes
const app = express();
app.get('/', (req, res) => {
const authCodeUrlParameters = {
scopes: ["user.read","offline_access"],
redirectUri: REDIRECT_URI,
prompt:'consent'
};
// get url to sign user in and consent to scopes needed for application
pca.getAuthCodeUrl(authCodeUrlParameters).then((response) => {
res.redirect(response);
}).catch((error) => console.log(JSON.stringify(error)));
});
app.get('/redirect', (req, res) => {
const tokenRequest = {
code: req.query.code,
scopes: ["user.read","offline_access"],
redirectUri: REDIRECT_URI,
accessType: 'offline',
};
pca.acquireTokenByCode(tokenRequest).then((response) => {
const accessToken = response.accessToken;
const refreshToken = () => {
const tokenCache = pca.getTokenCache().serialize();
const refreshTokenObject = (JSON.parse(tokenCache)).RefreshToken
const refreshToken = refreshTokenObject[Object.keys(refreshTokenObject)[0]].secret;
return refreshToken;
}
const tokens = {
accessToken,
refreshToken:refreshToken()
}
console.log(tokens)
res.sendStatus(200);
}).catch((error) => {
console.log(error);
res.status(500).send(error);
});
});
app.listen(SERVER_PORT, () => console.log(`Msal Node Auth Code Sample app listening on port ${SERVER_PORT}!`))
msal-node does not expose the refresh token to the end user by design. It is stored and used internally under the hood when you need a new access token. You should call acquireTokenSilent each time you need an access token and msal-node will manage the tokens by either returning a cached token to you or using the refresh token to acquire a new access token.
For more context: https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/2836
In addition to the accepted answer, its important to note that the MSAL cache can have many authenticated users (and lots of refresh tokens). Here is my solution to extract the exact refresh token for a specific user.
I use this on each login to exact the individuals refresh token and store it.
public extractRefresh = ( homeAccountId : string ) : string =>
{
try
{
const tokenCache = this.msalClientApp.getTokenCache().serialize();
const refreshTokenObject = ( JSON.parse( tokenCache ) ).RefreshToken;
let refreshToken = '';
Object.entries( refreshTokenObject ).forEach( ( item : any ) =>
{
if ( item[1].home_account_id === homeAccountId )
{
refreshToken = item[1].secret;
}
});
return refreshToken;
}
catch
{
return '';
}
}

How to connect my G-Suite in nodejs server using Gmail API

I want to get access to my G-Suite account in a nodejs server, using Gmail API.
I understood I should create Service Account and authenticate with its credentials.
I tried many examples and ways but couldn't make it works.
This is the last try I've made.
returns 400 bad request.
code: 400,
errors: [
{
domain: 'global',
reason: 'failedPrecondition',
message: 'Bad Request'
}
]
const {GoogleAuth} = require('google-auth-library');
const credentials = require('./sevice-account-credentials.json');
async function main() {
const clientEmail = credentials.client_email;
const privateKey = credentials.private_key;
if (!clientEmail || !privateKey) {
throw new Error(`
The CLIENT_EMAIL and PRIVATE_KEY environment variables are required for
this sample.
`);
}
const auth = new GoogleAuth({
credentials: {
client_email: clientEmail,
private_key: privateKey,
},
scopes: 'https://mail.google.com/',
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
const url = `https://www.googleapis.com/gmail/v1/users/my-gsuite#domain.co.il/labels/label_id`;
const res = await client.request({url});
console.log(res.data);
}
main().catch(console.error);
Issue:
You are not impersonating any account in the domain. That's the point of domain-wide delegation: impersonating / acting on behalf of another account.
Solution:
You have to specify which account you want the Service Account to act on behalf of, by providing the property clientOptions when instantiating GoogleAuth:
clientOptions: { subject: "my-gsuite#domain.co.il" }
So it would be like:
const auth = new GoogleAuth({
credentials: {
client_email: clientEmail,
private_key: privateKey,
},
scopes: 'https://mail.google.com/',
clientOptions: { subject: "my-gsuite#domain.co.il" }
});
Reference:
GoogleAuthOptions

Dialogflow easy way for authorization

Does exist an easy way to connect Dialogflow agent to node.js code? When I use this code with the correct projectID taken from the Dialogflow agent's settings page, I have the following error:
Error: Unexpected error while acquiring application default credentials: Could not load the default credentials. Browse to https://developers.google.com/accounts/docs/application-default-credentials for more information.
const sessionClient = new dialogflow.SessionsClient();
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
I visited the page, but for what I want I think is quite confused (they quotes other API and a lot of setting), how can I solve this?
I want to take informations from a file and loading all without installing third party APIs.
It is not very well documented, but the easiest way to authenticate is using the JSON file provided on your google cloud platform console.
const sessionClient = new dialogflow.SessionsClient({
keyFilename: '/path/to/google.json'
});
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
This also works for all the other clients. ContextsClients, EntityTypesClient and so on.
I am writing the code, which worked for me. Please follow all the steps provided in Reference link 2 and for coding purpose you can use the snippet provided.
I have also added the sample JSON of Google Cloud Oauth
References:
https://www.npmjs.com/package/dialogflow#samples
https://medium.com/#tzahi/how-to-setup-dialogflow-v2-authentication-programmatically-with-node-js-b37fa4815d89
//Downloaded JSON format
{
"type": "service_account",
"project_id": "mybot",
"private_key_id": "123456asd",
"private_key": "YOURKEY",
"client_email": "yourID#mybot.iam.gserviceaccount.com",
"client_id": "098091234",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/yourID%40mybot.iam.gserviceaccount.com"
}
//------*********************---------------------------
//
const projectId = 'mybot';
//https://dialogflow.com/docs/agents#settings
// generate session id (currently hard coded)
const sessionId = '981dbc33-7c54-5419-2cce-edf90efd2170';
const query = 'hello';
const languageCode = 'en-US';
// Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
let privateKey = 'YourKey';
// as per goolgle json
let clientEmail = "yourID#mybot.iam.gserviceaccount.com";
let config = {
credentials: {
private_key: privateKey,
client_email: clientEmail
}
}
const sessionClient = new dialogflow.SessionsClient(config);
// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: query,
languageCode: languageCode,
},
},
};
// Send request and log result
sessionClient
.detectIntent(request)
.then(responses => {
console.log('Detected intent');
const result = responses[0].queryResult;
console.log(` Query: ${result.queryText}`);
console.log(` Response: ${result.fulfillmentText}`);
if (result.intent) {
console.log(` Intent: ${result.intent.displayName}`);
} else {
console.log(` No intent matched.`);
}
})
.catch(err => {
console.error('ERROR:', err);
});
I have the same issue few months ago, check this, this is how i solve it.
From your JSON that Google Cloud extract this lines.
const dialogflow = require('dialogflow');
const LANGUAGE_CODE = 'en-US'
const projectId = 'projectid';
const sessionId = 'sessionId';
const query = 'text to check';
let privateKey = "private key JSON";
let clientEmail = "email acount from JSON";
let config = {
credentials: {
private_key: privateKey,
client_email: clientEmail
}
};
sessionClient = new dialogflow.SessionsClient(config);
async function sendTextMessageToDialogFlow(textMessage, sessionId) {
// Define session path
const sessionPath = this.sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: textMessage,
languageCode: LANGUAGE_CODE
}
}
}
try {
let responses = await this.sessionClient.detectIntent(request)
console.log('DialogFlow.sendTextMessageToDialogFlow: Detected intent', responses);
return responses
} catch (err) {
console.error('DialogFlow.sendTextMessageToDialogFlow ERROR:', err);
throw err
}
};
sendTextMessageToDialogFlow(query, sessionId)
Since the original question, the documentation for Dialogflow authentication has been improved. You should find all your answers here:
Authentication and access contro
I follow the above solutions with little changes :
// A unique identifier for the given session
const sessionId = uuid.v4();
// Create a new session
const sessionClient = new dialogflow.SessionsClient({
keyFilename: require("path").join('config/google-credential.json')
});
const sessionPath = sessionClient.sessionPath(process.env.DIALOGFLOW_PROJECTID, sessionId);

google-auth-library v.1.0 how to use just for identity?

I,ve been using google-auth-library#0.10.0 nodejs just for verifying user identity in my api services, now it changed to 1.0 and everything is broken.
I previously used example from here:
https://developers.google.com/identity/sign-in/web/backend-auth
now I cannot figure out how to verify identity using the new library.
Examples here: https://github.com/google/google-auth-library-nodejs
explains how to get access to google apis, I just need to verify identity.
this is my code:
const GoogleAuth = require('google-auth-library');
const auth = new GoogleAuth.GoogleAuth();
const google = require('googleapis');
const authData = {
'googleAuth': {
'clientID': 'xxxxxxxxxxx-aaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com',
'clientSecret': 'sssssssssssssssssssssssss',
'callbackURL': 'http://localhost:121212/auth/'
}
};
const CLIENT_ID = authData.googleAuth.clientID;
function verifyToken(token) {
let tokenPromise = new Promise(function(resolve, reject) {
client.verifyIdToken(
token,
CLIENT_ID,
// Or, if multiple clients access the backend:
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3],
function(e, login) {
if (e) {
return reject(e);
} else {
var payload = login.getPayload();
var userid = payload['sub'];
//console.log(payload, userid, e, login);
return resolve(login);
// If request specified a G Suite domain:
//var domain = payload['hd'];
}
});
});
return tokenPromise;
};
it was working fine, just to get googleUserId.
now I've this error:
Error: This method accepts an options object as the first parameter, which includes the idToken, audience, and maxExpiry.
I understand I'm not passing properly parameters and maybe its not the method to use..but how can I know which method and how it accepts parameters??? I cannot find any documentation about google-auth-library#1.0...some one is using it in production??
The verifyIdToken changed. Here's a working example.
import { OAuth2Client } from 'google-auth-library';
const client = new OAuth2Client(
'xxxxxxxxxxx-aaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com,
'sssssssssssssssssssssssss',
'http://localhost:121212/auth/',
);
client.verifyIdToken({
idToken: TOKEN,
audience: CLIENT_ID_1 // If you have multiple [CLIENT_ID_1, CLIENT_ID_2, ...]
}, (err, login) => {
console.log(login);
});
Just tested this recently.
Google needs to update their documentation.
Please note that you can specify a maxAge to the verify id token options as well.
Here's the interface copied from the source code
export interface VerifyIdTokenOptions {
idToken: string;
audience: string|string[];
maxExpiry?: number;
}
As you can see the idToken and audience are required, the maxExpiry is optional.
Source can be found here

Resources