I wanted to know if it was possible possible to use a service account to request data from any google API but using this library: https://github.com/google/google-api-javascript-client
I managed to find out how to use the library with OAuth2.0 credential from google cloud console.
But my real need requires me to use a service account to fetch these data.
Here's the code I used to fetch data from OAuth2.0 credentials:
initClient() {
return gapi.client.init({
apiKey: this.GSC_API_KEY, // already defined in the application
client_id:
"xxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
scope:
"https://www.googleapis.com/auth/webmasters https://www.googleapis.com/auth/webmasters.readonly",
discoveryDocs: [
"https://www.googleapis.com/discovery/v1/apis/webmasters/v3/rest"
]
});
},
gapiList() {
this.initClient()
.then(() => {
// Executes an API request, and returns a Promise.
// The method name `webmasters.sites.list` comes from the API webmasters.
return gapi.client.webmasters.sites.list();
})
.then(
response => {
console.log(response.body);
},
err => {
console.error(err.details);
}
);
},
Here's the code that request the API:
gapi.load("client", this.gapiList);
It does return me good data.
But my final purpose requires me to use a service account.
The initClient function does need a client_id to load correctly. If I'm giving the client_id of the service account it does return me an error.
"Not a valid origin for the client: http://localhost:8080/ has not been whitelisted for client ID xxxxxxxxxxxxx. Please go to https://console.developers.google.com/ and whitelist this origin for your project's client ID.
The error message is telling me to whitelist the localhost (where I am currently working) but I don't find how to whitelist localhost for a service account.
Hope I gave enough informations.
Thank for any reply and help.
As the message tells, you need to allow localhost:8080 as an origin to call the API. To protect you and your users, Google restricts your OAuth 2.0 application to using Authorized Domains. If you have verified the domain with Google, you can use any Top Private Domain as an Authorized Domain.
After you add an Authorized Domain, you can use any of its subdomains or pages, and any other associated country codes. Add your Authorized Domains before you add your redirect or origin URIs, your homepage URL, your terms of service URL, or your privacy policy URL.
To accomplish this, follow these steps:
In the GCP Console, click APIs & Services and then OAuth consent screen. You might have to click Menu Menu first.
In the Application name field, enter G Suite Migrate and click Save.
In the left menu, click Credentials.
Click Create credentials and then OAuth client ID.
Select Web application.
In the Name field, enter a name for the OAuth web client.
In the Authorized JavaScript origins field, enter the URL that you’ll use to access the G Suite Migrate platform (for example,
http://localhost:5131).
Click Create.
Make a note of the client ID shown in the Client ID field. You’ll need it when you set up the G Suite Migrate platform. Tip: You can
also access the client ID from APIs & Serviceand thenCredentials.
Click OK.
Note: It might take some time for the authorization process to
complete.
Extra: If you want a service account to be able to call an API on a users behalf, you will also need to delegate domain-wide authority to the service account. This is explained in detail here.
You can not use service account with Google JavaScript client library.
You should use to Oauth2 authentication. If you have to use service account you need to use server sided language such like node.js or python etc.
Related
I'm creating a simple web app that needs to call an Azure Function.
I've registered 2 apps in my Azure AD: one for my single page react application and another for my Azure Function.
The app for my azure function has an API exposed on it and a scope configured called CallApi.
The app itself has authentication configured on it (easy auth), and the client id matches the API app registration within AAD:
The app for my SPA has permission to request this scope and this is granted by default for all users.
I am able to successfully log users into my SPA and get an access token. I'm doing this with the use of the #azure/msal-browser and #azure/msal-react npm libraries, specifically:
<MsalAuthenticationTemplate interactionType={InteractionType.Redirect} authenticationRequest={{scopes: ["User.Read", "api://<redacted>/CallApi"]}} >
However, the access token that is returned only has these scopes: ['openid', 'profile', 'User.Read', 'email'] and not api://<redacted>/CallApi. So when I try to exchange my access token for a token by sending a POST request to https://<app_name>.azurewebsites.net/.auth/login/aad it returns an unauthorized message.
How can I ensure that the CallApi scope is allow by my app?
So when I try to exchange my access token for a token by sending a POST request to https://<app_name>.azurewebsites.net/.auth/login/aad it returns an unauthorized message.
Please ensure that the URL is correct as it should be in the format of <app-url>/.auth/login/aad/callback and note that the parameters are in the body of the HTTP POST request.
However, the access token that is returned only has these scopes: ['openid', 'profile', 'User.Read', 'email']
openid
If an app performs sign-in by using OpenID Connect, then it must request the openid scope. That is to say, the openid scope displays on the work account consent page as the “Sign you in” permission.
email
The email scope can work with the openid scope. As it gives the app access to the user’s primary email address in the form of the email claim. Where, the email claim is included in a token only if an email address is associated with the user account, which isn’t always the case.
profile
The profile scope can also work with the openid scope. This gives the app access to a substantial amount of information about the user. The information that it has access includes, the user’s given name, surname, preferred username, and object ID.
In the Microsoft ecosystem there are some high-privilege permissions that can be set to admin-restricted. This include:
Firstly, read all user’s full profiles by using User.Read.All
Secondly, write data to an organization’s directory by using Directory.ReadWrite.All
Lastly, read all groups in an organization’s directory by using Groups.Read.All
Please refer this article for an example regarding the POST request regarding the Call API Scope.
Generally, there are at least 2 options of securing applications using Keycloak OpenID Connect stack:
Use it on the application using Keycloak adapters (in my case, a SPA javascript front end)
Use it on Apache using mod_auth_openidc
If I choose to use number 2, how can I obtain the user data (username, for example)?
------------------------Edit due to #Cyril Dangerville answer -----------------
While I understand the general approach, my way is seemed very forced.
Now my authenticated, my request has session cookie and access token headers, but cannot access any header due to being initial page load
Perform an call to any protected resource to get access Header value
Use the access token to call the userinfo end point (But this is on another domain so the cookie wont get submitted)
Can you help me by explain where I should go here?
With mod_auth_openidc, you pass user data to applications as Apache environment variables and/or HTTP headers. The latter is useful if using Apache as reverse proxy for remote apps (mod_proxy). The proxy case is addressed briefly in mod_auth_openidc project's FAQ.
You can find more details about how to configure this translation of OpenID Connect token claims - including the authenticated user data - to environment variables/headers in the configuration file: auth_openidc.conf; two properties in particular:
For the REMOTE_USER variable: OIDCOAuthRemoteUserClaim <claim-name> [<regular-expression>]
For others: OIDCPassClaimsAs [none|headers|environment|both].
I'm currently implementing OAuth login with LinkedIn in my React and Play app and am running into a CORS error when trying to redirect to the authorization page in my dev environment:
XMLHttpRequest cannot load https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_i…basicprofile&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fusers%2Flinkedin. Redirect from 'https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_i…basicprofile&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fusers%2Flinkedin' to 'https://www.linkedin.com/uas/login?session_redirect=%2Foauth%2Fv2%2Flogin-s…' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
I have the following setup:
Play server running at localhost:9000
React app (created via create-react-app) running at localhost:3000
My JS code calls the /auth/linkedin endpoint which is implemented as follows:
Action { implicit req: RequestHeader =>
val csrfToken = CSRF.getToken.get.value
Redirect(linkedinUrl(oauthConfig.linkedinClientId, csrfToken)).withSession("state" -> csrfToken)
}
I have my Play application set to handle CORS appropriately.
My react app just makes a request to the above endpoint via Axios:
axios.get('/auth/linkedin')
This responds with a 303 with a redirect to the LinkedIn auth page which then gives me the error.
How do I get the CORS policy working correctly in this dev setup? I've tried adding the following to my package.json as the create-react-app documentation recommends:
"proxy": "http://localhost:9000",
And I've also tried setting a request header to "Access-Control-Allow-Origin" : "*" on the redirect in the Play server with no success.
Note that going to localhost:9000/auth/linkedin redirects properly.
https://www.linkedin.com/oauth/v2/authorization responses apparently don’t include the Access-Control-Allow-Origin response header, and because they do not, your browser blocks your frontend JavaScript code from accessing the responses.
There are no changes you can make to your own frontend JavaScript code nor backend config settings that’ll allow your frontend JavaScript code to make requests the way you’re trying directly to https://www.linkedin.com/oauth/v2/authorization and get responses back.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS explains in more detail but the gist of it is: for CORS, the server the request is being sent to must be configured to send the Access-Control-Allow-Origin response header, nor your own backend server.
2019-05-30 update
The current state of things seems to be that when needing to do LinkedIn authorization, you’ll have to initiate the request from your backend code. There’s no way you can do it from your frontend code, because LinkedIn no longer provides any support for it at all.
LinkedIn did previously provide some support for handling it from frontend code. But the page that documented it, https://developer.linkedin.com/docs/getting-started-js-sdk, now has this:
The JavaScript SDK is not currently supported
And https://engineering.linkedin.com/blog/2018/12/developer-program-updates has this:
Our JavaScript and Mobile Software Development Kits (SDKs) will stop working. Developers will need to migrate to using OAuth 2.0 directly from their apps.
So the remainder of this answer (from 2017-06-13) has now become obsolete. But it’s preserved below for the sake of keeping the history complete.
2017-06-13 details, now obsoleted
Anyway https://developer.linkedin.com/docs/getting-started-js-sdk has official docs that explain how to request authorization for a user cross-origin, which appears to be just this:
<script type="text/javascript" src="//platform.linkedin.com/in.js">
api_key: [API_KEY]
onLoad: [ONLOAD]
authorize: [AUTHORIZE]
lang: [LANG_LOCALE]
IN.User.authorize(callbackFunction, callbackScope);
</script>
And https://developer.linkedin.com/docs/signin-with-linkedin has docs for another auth flow:
<script type="in/Login"></script> <!-- Create the "Sign In with LinkedIn" button-->
<!-- Handle async authentication & retrieve basic member data -->
<script type="text/javascript">
// Setup an event listener to make an API call once auth is complete
function onLinkedInLoad() {
IN.Event.on(IN, "auth", getProfileData);
}
// Handle the successful return from the API call
function onSuccess(data) {
console.log(data);
}
// Handle an error response from the API call
function onError(error) {
console.log(error);
}
// Use the API call wrapper to request the member's basic profile data
function getProfileData() {
IN.API.Raw("/people/~").result(onSuccess).error(onError);
}
</script>
I ran into a similar problem, so let's divide this problem into detailed steps
Hit request to get the code(from frontend)
now send this code to the backend
In the backend, make another call to LinkedIn OAuth API and get the access token
With this access token make 3 separate calls to get the name, profile picture
and email of the user(yes you heard that right you need to make 3 separate calls and also the response JSON format is not very appealing)
Visit this for the detailed step-by-step process, it involves a lot of things. I can just share the process here but for the actual implementation visit this.
https://www.wellhow.online/2021/04/setting-up-linkedin-oauth-and-fixing.html
What could be done is:
window.location.href='http://localhost:9000/auth/linkedin'
The urlEndPoint could be directly to linkedIn's API or a back-end service which makes the call to linkedIn's API.
I'm trying to create a web page that sends an email only using javascript and Gmail API. I have set up my developer's console to use the API and also created an API key and a credential.
I have adapted this example code, and so far I think I got to the point of loading the GMail API. (or.. 400 means something is wrong?) The console log shows:
POST https://content.googleapis.com/gmail/v1/users/me/messages/send?alt=json 400 (OK)
zu # cb=gapi.loaded_0:85
n # cb=gapi.loaded_0:85
Cu # cb=gapi.loaded_0:85
(anonymous function) # cb=gapi.loaded_0:86
g # cb=gapi.loaded_0:55
c # cb=gapi.loaded_0:46
And when I try authenticating with oAuth2.0 through a url:
var code;
$.get("https://accounts.google.com/o/oauth2/auth?scope=email&
redirect_uri=http://hiro.collegewebserver.com/COSW200/hw13/gmailtest.html&
response_type=token&client_id=386373199858-o1rt7qj3gt99gbfg6pqpr0g6i92urq9t.apps.googleusercontent.com&
approval_prompt=force",
function (returnedValue) { code = returnedValue; });
I get this message:
XMLHttpRequest cannot load https://accounts.google.com/o/oauth2/auth?scope=email&
redirect_uri=http://h…3gt99gbfg6pqpr0g6i92urq9t.apps.googleusercontent.com&
approval_prompt=force.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://hiro.collegewebserver.com' is therefore not allowed access.
I've read other posts that say this may be due to cross origin requests being blocked? Someone wrote that there are ways to work around this like using JSONP?
If someone can enlighten me as to what I'm missing here, I would really appreciate! :)
You cannot make an ajax call to Google to get your access code. The idea of OAuth2 is that you first redirect the user to accounts.google.com/o/oauth2/your_settings. In your case:
https://accounts.google.com/o/oauth2/auth?scope=email&redirect_uri=http://hiro.collegewebserver.com/COSW200/hw13/gmailtest.html&response_type=token&client_id=386373199858-o1rt7qj3gt99gbfg6pqpr0g6i92urq9t.apps.googleusercontent.com&approval_prompt=force
In this Google environment the user can login with his/her account. One of the settings you provide is the redirect_uri.
Since you whitelisted http://hiro.collegewebserver.com/COSW200/hw13/ as one of the Authorized redirect URIs in your Google Developers Console and this url is related to your client_id, no other application than your website can use your client_id to receive an authorization code.
After login the user will be redirected to this redirect_uri, with the code as one of the parameters.
This will look something like this:
http://hiro.collegewebserver.com/COSW200/hw13/#access_token=yb27…jK0AVtilhnrJDcuTISgIB5LiNtKLMut1kVvPW69w&token_type=Bearer&expires_in=3600
After that you would have to extract the access token from the url. With this access token you can authorise the user and make use of the scopes you entered. This can be done with an ajax call.
$http({
method: 'GET',
url: 'https://www.googleapis.com/upload/gmail/v1/users/userId/messages?access_token=' + access_token
}).then(function(response){
//your code
})
You could also put the access_token in the header of the call:
Authorization: Bearer access_token
Another option is to use the API Client Library for JavaScript (Beta): https://developers.google.com/api-client-library/javascript
Although it is in Beta and the basic workflow is the same, it might give you clearer examples.
I am currently in the process of implementing a server-side OAuth2 flow in order to authorize my application.
The JS application will be displaying YouTube Analytics data on behalf of a registered CMS account to an end user (who own's a channel partnered with the CMS account). As a result of this, the authorization stage needs to be completely hidden from the user. I am attempting to authorize once, then use the 'permanent' authorization code to retrieve access tokens as and when they're needed.
I am able to successfully authorize, and retrieve an access code. The problem begins when i attempt to exchange the access code for a token.
The HTTP POST Request to achieve this needs to look like this...
POST /o/oauth2/token HTTP/1.1
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=8819981768.apps.googleusercontent.com&
client_secret={client_secret}&
redirect_uri=https://oauth2-login-demo.appspot.com/code&
grant_type=authorization_code
I am using this code to achieve this:
var myPOSTRequest = new XMLHttpRequest();
myPOSTRequest.open('POST', 'https://accounts.google.com/o/oauth2/token', true);
myPOSTRequest.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
myPOSTRequest.send('code=' + myAuthCode + '&redirect_uri=http%3A%2F%2Flocalhost%2FCMSAuth3.html&client_id=626544306690-kn5m3vu0dcgb17au6m6pmr4giluf1cle.apps.googleusercontent.com&scope=&client_secret={my_client_secret}&grant_type=authorization_code');
I can successfully get a 200 OK response to this Request however no access token is returned, and myPOSTRequest.responseText returns an empty string.
I have played with Google's OAuth Playground - and can successfully get a token using my own credentials.
Am i missing something here?
You cannot do this, because there is the same origin policy. This is a security concept of modern browsers, which prevents javascript to get responses from another origin, than your site. This is an important concept, because it gives you the ability, to protect you against CSRF. So don't use the code authorization flow, use instead the token authorization flow.
Try and build up the full URL. Then dump it in a webbrowser. If its corect you will get the json back. You have the corect format.
https://accounts.google.com/o/oauth2/token?code=<myAuthCode>&redirect_uri=<FromGoogleAPIS>&client_id=<clientID>&client_secret={my_client_secret}&grant_type=authorization_code
Other things to check:
Make sure that you are using the same redirect_uri that is set up in google apis.
How are you getting the Authcode back? If you are riping it from the title of the page i have had issues with it not returning the full authcode in the title try checking the body of the page. This doesnt happen all the time. I just ocationally.