I want to consume google adsense api using a js script. I don't have a running server and I just want to fetch some data using an access token.
From the documentation I saw that I have to use the consent screen in order to get the authorization code in order to get the tokens.
Is there a way to do everything programmatically? without any redirects and consent screens.
const { clientId, clientSecret } = this.query;
const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, ''); // without redirect uri
const scope = ['https://www.googleapis.com/auth/adsense', 'https://www.googleapis.com/auth/adsense.readonly'];
const url = oauth2Client.generateAuthUrl({
scope,
});
const { tokens } = await oauth2Client.getToken('code'); // how to get the code without using the consent screen
Related
Probleme
I want to create a server application that modifies playlists on spotify, deezer and youtube (to create multi-platform playlists). My problem is that these different services require OAuth2 authentication to manipulate playlists, and that this authentication requires human intervention
What I've tried
I tried to use the api key, but it did not allow me to edit the playlist (access_key needed).
My Research
from what I understood from the different documentation, the api key is made for server applications (like my case) and that the OAuth2 authentication is more made to authenticate a client.
Question
how to authenticate a server side application with an OAuth2 flow. or how to allow a server application to modify/manipulate playlists on youtube, spotify, deezer.
Details
i use node server.
The first thing you need to under stand is the difference between public and private data. Private data is data that is not owned by anyone that any one has access to. Public videos on YouTube for example can be read by anyone. Private user data is data that is owned by a user that being their play lists for example.
API keys will only give you access to public data not private user data.
The only flow available to you to read YouTube private user data is Ouath2. What you should do is follow the official node.js sample.
When you run this sample it is single user it will store the user credentials in TOKEN_DIR. Then it will use the refresh token that was generated in order to request a new access token when ever it needs one. So you will need to authorize your code once then it will be able to run on your server without you having to grant it permissions.
var fs = require('fs');
var readline = require('readline');
var {google} = require('googleapis');
var OAuth2 = google.auth.OAuth2;
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/youtube-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/youtube.readonly'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'youtube-nodejs-quickstart.json';
// Load client secrets from a local file.
fs.readFile('client_secret.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the YouTube API.
authorize(JSON.parse(content), getChannel);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
var clientSecret = credentials.installed.client_secret;
var clientId = credentials.installed.client_id;
var redirectUrl = credentials.installed.redirect_uris[0];
var oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function(err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* #param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function(code) {
rl.close();
oauth2Client.getToken(code, function(err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
/**
* Store token to disk be used in later program executions.
*
* #param {Object} token The token to store to disk.
*/
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) throw err;
console.log('Token stored to ' + TOKEN_PATH);
});
}
/**
* Lists the names and IDs of up to 10 files.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function getChannel(auth) {
var service = google.youtube('v3');
service.channels.list({
auth: auth,
part: 'snippet,contentDetails,statistics',
forUsername: 'GoogleDevelopers'
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var channels = response.data.items;
if (channels.length == 0) {
console.log('No channel found.');
} else {
console.log('This channel\'s ID is %s. Its title is \'%s\', and ' +
'it has %s views.',
channels[0].id,
channels[0].snippet.title,
channels[0].statistics.viewCount);
}
});
}
You may want to monitor it there are some instances where a refresh token will be expired in the event that happens you will need to authorize the application again.
An OAuth 2 application delegates the authentication to services (eg. Google, Spotify, Amazon, Github, LinkedIn, etc.) that host a user account and asks for (limited) authorization from those services, after the user has given consent.
There is a difference between authentication and authorization.
Authentication makes sure a user is who they're claiming to be. Whereas Authorization governs what the user has access to.
So your OAuth2 application is leveraging Spotify social account integration to delegate authentication to Spotify and in turn receiving limited authorization to modify playlists (only).
To answer your question: To be able to perform any action (eg. modify Spotify playlists) you'd need the access token from Spotify API. Here's what the process looks like:
User logs in to your website using their Spotify account.
User is redirected to the consent screen. They give approval for the permissions your application is asking for (i.e., modify playlist permission).
Once the user consents, your application receives an access token (along with a refresh token, and profile information).
Your application then uses the provided access token to modify the user playlists.
Passportjs is a very well built library around OAuth2 and it has support for Spotify OAuth2. I have recently documented about the complete process to implement multi-provider OAuth2 in node.js along with the explanations.
I am getting this error from Stripe when I try to create a new source for the selected bank account. I am using the new (beta) version of the Plaid Node SDK. Here is my code:
let user;
const mode = "sandbox";
const dsService = new CaspioDsService();
// Load the user if not already loaded by cognitoAuth
if (!req.user) {
user = new User(
dsService,
new CaspioRefDataService(),
new AuthUserService({
organizationId: res.locals.organization.Organization_ID,
isAuthenticated: res.locals.isAuthenticated,
})
);
await user.load(req.params.userId);
} else {
user = req.user.userObject;
}
const configuration = new Configuration({
basePath: PlaidEnvironments[mode],
baseOptions: {
headers: {
"PLAID-CLIENT-ID": config.plaid.clientId,
"PLAID-SECRET": mode === "sandbox" ? config.plaid.secretSandbox : config.plaid.secretProduction,
"Plaid-Version": "2020-09-14",
},
},
});
const plaidClient = new PlaidApi(configuration);
console.log(configuration.basePath); // https://sandbox.plaid.com
// Exchange the public token for the Plaid access token
const plaidTokenRes = await plaidClient.itemPublicTokenExchange({
public_token: req.body.publicToken,
});
const accessToken = plaidTokenRes.data.access_token;
console.log(accessToken); // access-sandbox-d92396c2-1f49-4780-9ae9-23d50645f364
// Get the Stripe bank account token from Plaid
const stripeTokenRes = await plaidClient.processorStripeBankAccountTokenCreate({
access_token: accessToken,
account_id: req.body.accountId
});
const bankAccountToken = stripeTokenRes.data.stripe_bank_account_token;
console.log(bankAccountToken); // btok_1JFMGwGq7ejZoSiwGmM8WSSm
let stripeCustomerId = user.getStripeToken();
const stripeClient = await StripeHelper.getStripeClient(mode); // Get Stripe client in sandbox mode
console.log(stripeCustomerId); // cus_Jt7AWZjC8rHPzt
// Add the source to the Stripe customer and get the bank account info
const bankAccount = await stripeClient.customers.createSource(stripeCustomerId, {
source: bankAccountToken,
}); // Error: No such token: 'btok_1JFMGwGq7ejZoSiwGmM8WSSm'
Any ideas what I might be doing wrong? I expect the issue is with my code, or possibly Plaid (I don't think it is a Stripe problem).
It sounds like you're getting a token from Plaid, but Stripe is rejecting it, which suggests a problem with the relationship between your Plaid and Stripe setups. Are you sure that you enabled the Plaid/Stripe integration in the Plaid dashboard and that the client id / secret you're using matches the Plaid account where the integration is enabled? The Plaid docs also suggest that this error can be caused by using a mismatched set of environments (e.g. using Production with Stripe but Sandbox with Plaid).
The problem was that we had the wrong Stripe account connected. Silly one, but I'm posting this in case anyone else makes the same mistake.
I have have run the examples from https://github.com/dart-lang/googleapis_examples/blob/master/drive_upload_download_console/bin/main.dart.
The example creates an authenticated HTTP client for accessing Google Drive API with:
import 'package:googleapis_auth/auth_io.dart' as auth;
…
…
auth.clientViaUserConsent(identifier, scopes, userPrompt).then((client) { // with client_id, client_secret, scope
var api = new drive.DriveApi(client);
…
…
}
When I run the example I have to give the User consent in a web browser each time I run the example above.
I want to create an authenticated HTTP client without having to use the user consent fonction (auth.clientViaUserConsent) but with a stored acces token or the refresh token. How can I create such an authenticated HTTP client? With the googleapis_auth package?(https://pub.dartlang.org/packages/googleapis_auth)
You are already there.
Your client object contains all you need already.
Here is your modified code using the stored credentials to inquire about freeBusy time:
auth.clientViaUserConsent(identifier, scopes, userPrompt).then((client) { // with client_id, client_secret, scope
var api = new drive.DriveApi(client);
debugPrint(' access token: ' + client.credentials.accessToken.data +' refresh token ' + client.credentials.refreshToken);
// store the tokens in the apps key store
}
At some time in the future make a new call to obtain new access credentials from the never expiring refresh token, and create a new client for your purposes.
AccessCredentials _fromStorage = AccessCredentials(client.credentials.accessToken,
client.credentials.refreshToken, _scopes );
var _newClient = new http.Client();
AccessCredentials _accessCredentials = await refreshCredentials( _clientID, _fromStorage , _newClient);
_newClient = authenticatedClient(_newClient, _accessCredentials);
// the code below was just for me to test this out with my API scopes. replace with your code
var calendar = cal.CalendarApi(_newClient);
String calendarId = "---some string---";
cal.FreeBusyRequest _request = cal.FreeBusyRequest.fromJson(
{
'items': [
{'id': calendarId, 'busy': 'Active'}
],
'timeMin': (new DateTime(2020, 11, 17)).toIso8601String()+'Z',
'timeMax': (new DateTime(2020, 11, 19)).toIso8601String()+'Z'
});
debugPrint('request: ' + _request.toJson().toString());
cal.FreeBusyResponse response = await calendar.freebusy.query(_request);
debugPrint(response.toJson().toString());
});
In my c# desktop app, I'm using Google's API to authenticate and retrieve the access token for an API. I noticed that the API will cache this token and use it again until it expires.
Using my browser, I was able to revoke the token using:
https://accounts.google.com/o/oauth2/revoke?token=1/xxxxxxx
I did this to test out how the API handles revoked tokens. As exepected, the API fails. The problem I have though is getting the API to use a new token. It continues to retrieve the cached token, even though it's been revoked. The following code is used to authenticate the use of the API:
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, Scopes, "user", CancellationToken.None);
How can I get the API to remove the cached token and request a new one?
You have to log out before getting a new token. You can read about OAuth 2.0 here https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth?hl=uk .
Below how I've done that for my windows phone 8 application:
var string const GoogleTokenFileName = "Google.Apis.Auth.OAuth2.Responses.TokenResponse-user"
public async Task LoginAsync()
{
await Logout();
_credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets
{
ClientId = _xmlfile.GoogleClientId,
ClientSecret = _xmlfile.GoogleClientSecret
}, new[] { Oauth2Service.Scope.UserinfoProfile }, "user", CancellationToken.None);
return session;
}
public async Task Logout()
{
await new WebBrowser().ClearCookiesAsync();
if (_storageService.FileExists(GoogleTokenFileName))
{
_storageService.DeleteFile(GoogleTokenFileName);
}
}
Hope it helps.
I can't seem to find any documentation on how to restrict the login to my web application (which uses OAuth2.0 and Google APIs) to only accept authentication requests from users with an email on a specific domain name or set of domain names. I would like to whitelist as opposed to blacklist.
Does anyone have suggestions on how to do this, documentation on the officially accepted method of doing so, or an easy, secure work around?
For the record, I do not know any info about the user until they attempt to log in through Google's OAuth authentication. All I receive back is the basic user info and email.
So I've got an answer for you. In the OAuth request you can add hd=example.com and it will restrict authentication to users from that domain (I don't know if you can do multiple domains). You can find hd parameter documented here
I'm using the Google API libraries from here: http://code.google.com/p/google-api-php-client/wiki/OAuth2 so I had to manually edit the /auth/apiOAuth2.php file to this:
public function createAuthUrl($scope) {
$params = array(
'response_type=code',
'redirect_uri=' . urlencode($this->redirectUri),
'client_id=' . urlencode($this->clientId),
'scope=' . urlencode($scope),
'access_type=' . urlencode($this->accessType),
'approval_prompt=' . urlencode($this->approvalPrompt),
'hd=example.com'
);
if (isset($this->state)) {
$params[] = 'state=' . urlencode($this->state);
}
$params = implode('&', $params);
return self::OAUTH2_AUTH_URL . "?$params";
}
I'm still working on this app and found this, which may be the more correct answer to this question. https://developers.google.com/google-apps/profiles/
Client Side:
Using the auth2 init function, you can pass the hosted_domain parameter to restrict the accounts listed on the signin popup to those matching your hosted_domain. You can see this in the documentation here: https://developers.google.com/identity/sign-in/web/reference
Server Side:
Even with a restricted client-side list you will need to verify that the id_token matches the hosted domain you specified. For some implementations this means checking the hd attribute you receive from Google after verifying the token.
Full Stack Example:
Web Code:
gapi.load('auth2', function () {
// init auth2 with your hosted_domain
// only matching accounts will show up in the list or be accepted
var auth2 = gapi.auth2.init({
client_id: "your-client-id.apps.googleusercontent.com",
hosted_domain: 'your-special-domain.example'
});
// setup your signin button
auth2.attachClickHandler(yourButtonElement, {});
// when the current user changes
auth2.currentUser.listen(function (user) {
// if the user is signed in
if (user && user.isSignedIn()) {
// validate the token on your server,
// your server will need to double check that the
// `hd` matches your specified `hosted_domain`;
validateTokenOnYourServer(user.getAuthResponse().id_token)
.then(function () {
console.log('yay');
})
.catch(function (err) {
auth2.then(function() { auth2.signOut(); });
});
}
});
});
Server Code (using googles Node.js library):
If you're not using Node.js you can view other examples here: https://developers.google.com/identity/sign-in/web/backend-auth
const GoogleAuth = require('google-auth-library');
const Auth = new GoogleAuth();
const authData = JSON.parse(fs.readFileSync(your_auth_creds_json_file));
const oauth = new Auth.OAuth2(authData.web.client_id, authData.web.client_secret);
const acceptableISSs = new Set(
['accounts.google.com', 'https://accounts.google.com']
);
const validateToken = (token) => {
return new Promise((resolve, reject) => {
if (!token) {
reject();
}
oauth.verifyIdToken(token, null, (err, ticket) => {
if (err) {
return reject(err);
}
const payload = ticket.getPayload();
const tokenIsOK = payload &&
payload.aud === authData.web.client_id &&
new Date(payload.exp * 1000) > new Date() &&
acceptableISSs.has(payload.iss) &&
payload.hd === 'your-special-domain.example';
return tokenIsOK ? resolve() : reject();
});
});
};
When defining your provider, pass in a hash at the end with the 'hd' parameter. You can read up on that here. https://developers.google.com/accounts/docs/OpenIDConnect#hd-param
E.g., for config/initializers/devise.rb
config.omniauth :google_oauth2, 'identifier', 'key', {hd: 'yourdomain.com'}
Here's what I did using passport in node.js. profile is the user attempting to log in.
//passed, stringified email login
var emailString = String(profile.emails[0].value);
//the domain you want to whitelist
var yourDomain = '#google.com';
//check the x amount of characters including and after # symbol of passed user login.
//This means '#google.com' must be the final set of characters in the attempted login
var domain = emailString.substr(emailString.length - yourDomain.length);
//I send the user back to the login screen if domain does not match
if (domain != yourDomain)
return done(err);
Then just create logic to look for multiple domains instead of just one. I believe this method is secure because 1. the '#' symbol is not a valid character in the first or second part of an email address. I could not trick the function by creating an email address like mike#fake#google.com 2. In a traditional login system I could, but this email address could never exist in Google. If it's not a valid Google account, you can't login.
Since 2015 there has been a function in the library to set this without needing to edit the source of the library as in the workaround by aaron-bruce
Before generating the url just call setHostedDomain against your Google Client
$client->setHostedDomain("HOSTED DOMAIN")
For login with Google using Laravel Socialite
https://laravel.com/docs/8.x/socialite#optional-parameters
use Laravel\Socialite\Facades\Socialite;
return Socialite::driver('google')
->with(['hd' => 'pontomais.com.br'])
->redirect();