The CreatorCon Call for Content is officially open! Get started here.

Gsuite provides access token, but using it causes a 403: forbidden

Ward van Hoof
Mega Guru

Hi,

At my client, we would like ServiceNow to read the groups as they are registered in GSuite. I run into an issue with this integration, as i seem to receive a OAuth token, but when i use it i receive a 403 error

From the google docs, i understand that you should (more or less) "impersonate" a user. However, i'm not sure if this is mandatory when using a Service Account in google. Because google created a service account, i do not have a client secret and client ID, but had to compile a .jks file from a certificate and use a JWT integration.

Any assistance will be greatly appreciated, as currently I think i tried everything i could think off.

Below a description of my configuration.

Using the JWT Provider and Application registry, I registered the credentials our Google team provided. Using these credentials I am able to click on the Rest Message (sys_rest_message) "Get oauth Token" which will show "OAuth token flow completed successfully" in the pop up. When i click on "Test" on the HTTP Method (sys_rest_message_fn), however, I receive this: 

HTTP status: 403
Error message: Method failed: (/admin/directory/v1/groups) with code: 403 - Forbidden username/password combo
Error code: 6
Response:
{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "forbidden",
    "message": "Not Authorized to access this resource/api"
   }
  ],
  "code": 403,
  "message": "Not Authorized to access this resource/api"
 }
}

Some key configurations I have:

Endpoint: https://www.googleapis.com/admin/directory/v1/groups?domain=<CLIENT_DOMAIN>


X.509 Certificate:
Record with a .jks file for authentication

JWT Keys:
Links to the X.509 Certificate: 

OAuth Entity Scope:
Requesting the scope: https://www.googleapis.com/auth/admin.directory.group
NOTE: I verified that google grants the service accounts this scope. I also tried the .readonly version

OAuth Entity Profile:
Linking scope, Oauth Provider, JWT Provider

JWT Provider (blacked out parts is the ServiceAccount email provided by google)
1 Custom claim not pictured (scope: https://www.googleapis.com/auth/admin.directory.group) 

find_real_file.png

Application Registry (OAuth)
I had to empty Client ID & Client Secret using a background script, otherwise i would not receive a token, because they are illegal parameters
Oauth entity profile: links to the OAuth entity profile configured (as described above)

find_real_file.png

 

I also tried to test my configuration with this script to debug:

initiateFlow();

function initiateFlow() {
	gs.print('\nstart....');
	var requestor = 'd244b8f0dbdc0c106380ef905b961911';
	var requestor_context = 'sys_rest_message';
	var oauth_provider_profile = '3e285fd0dbdc4810be87553c689619f1';
	var oauth_provider_id = '';

	var tokenRequest = new  sn_auth.GlideOAuthClientRequest();
	tokenRequest.setParameter('oauth_requestor_context', requestor_context);
	tokenRequest.setParameter('oauth_requestor', requestor);
	tokenRequest.setParameter('oauth_provider_profile', oauth_provider_profile);		
	tokenRequest.setParameter('oauth_provider_id', oauth_provider_id);

	var oauthClient = new sn_auth.GlideOAuthClient();
	var tokenResponse = oauthClient.requestTokenByRequest(null, tokenRequest);
	var errorMsg = tokenResponse.getErrorMessage();

	gs.print('Response code: ' + tokenResponse.getResponseCode() + '\nErrorMessage: ' + errorMsg);

	if (tokenResponse) {
		var token = tokenResponse.getToken();
		if (token) {
			if (token.getAccessToken()) {
				gs.print('\nTOKEN RECEIVED:\n' + token.getAccessToken());
				makeRest(token.getAccessToken())
			}
		}
	}
}


function makeRest(recToken) {
	gs.print('\nMake rest using token');
	var endpoint = 'https://www.googleapis.com/admin/directory/v1/groups?domain=<CLIENT_DOMAIN>';

	var sm = new sn_ws.RESTMessageV2();
	sm.setEndpoint(endpoint);
	sm.setHttpMethod('get');
	sm.setRequestHeader('Authorization', 'Bearer ' + recToken);
	var response = sm.execute();
	gs.print('\n\nREST Response:\nBody:\n' + response.getBody() + '\nstatusCode: ' + response.getStatusCode());
}

Which results in this output

As you can see, it notes that a token is received (line 9-13)
All the way at the bottom, you can see the response is "403: forbidden"

*** Script: 
start....
Ignore oauth entity from request. Use provider from oauth entity profile.
Getting JWTProvider for jwtProviderSysId = 92eb2cf4db9c0c106380ef905b9619eb
Getting JWTProviderConfig for jwtProviderId = 92eb2cf4db9c0c106380ef905b9619eb
Started to generate JWT
Successfully generated JWT
StorageEncrypter: ignoring already encrypted text starting with: }iO:S...
*** Script: Response code: 200
ErrorMessage: null
*** Script: 
TOKEN RECEIVED:
ya29.c.Kl6iB3YTIpdRQYZUEF6YTBd_EGAkhhwnpKel_4qR7FopReqY4Mz0-3Ku-C7BT5AedA0oDiI3ehjOS4HVZ3LgaVvcI-Gs6yZrOhoWTsrFWj16zUlVYZQl_wnWd_wWmDjS
*** Script: 
Make rest using token
*** Start  Background transaction - system, user: system
*** Start  Background transaction - system, user: system
Starting: SMTP Sender 2.015a962b37020200daa9a16043990e68, Trigger Type: Interval, Priority: 25, Upgrade Safe: true, Repeat: 1 Minute
Name: SMTP Sender 2
Job Context: 
#Mon Oct 21 01:44:16 PDT 2019

Script: 

*** Start  Background transaction - system, user: system
*** Start  Background transaction - system, user: system
Starting: events process 1.a0e84303db133300a898f7871d9619ae, Trigger Type: Interval, Priority: 25, Upgrade Safe: true, Repeat: 10 Seconds
Name: events process 1
Job Context: 
#Mon Oct 21 01:45:41 PDT 2019
fcScriptName=javascript\:gs.processDelegatedEvents();

Script: 

Starting: Flow Engine Event Handler.1d160122db0000106380ef905b9619d3, Trigger Type: Repeat, Priority: 100, Upgrade Safe: true, Repeat: 2 Seconds
Name: Flow Engine Event Handler
Job Context: 
#Mon Oct 21 01:45:40 PDT 2019

Script: 

Starting: Flow Engine Event Handler.5d160122db0000106380ef905b9619d5, Trigger Type: Repeat, Priority: 100, Upgrade Safe: true, Repeat: 2 Seconds
Name: Flow Engine Event Handler
Job Context: 
#Mon Oct 21 01:45:40 PDT 2019

Script: 

*** Start  Background transaction - system, user: system
*** Start  Background transaction - system, user: system
Starting: Flow Engine Event Handler.65160122db0000106380ef905b9619d9, Trigger Type: Repeat, Priority: 100, Upgrade Safe: true, Repeat: 2 Seconds
Name: Flow Engine Event Handler
Job Context: 
#Mon Oct 21 01:45:40 PDT 2019

Script: 

Completed: SMTP Sender 2 in 0:00:00.004, next occurrence is 2019-10-21 10:47:01
*** Start  Background transaction - system, user: system
Starting: Flow Engine Event Handler.69160122db0000106380ef905b9619d7, Trigger Type: Repeat, Priority: 100, Upgrade Safe: true, Repeat: 2 Seconds
Name: Flow Engine Event Handler
Job Context: 
#Mon Oct 21 01:45:40 PDT 2019

Script: 

Completed: Flow Engine Event Handler in 0:00:00.005, next occurrence is 2019-10-21 10:45:53
Completed: Flow Engine Event Handler in 0:00:00.006, next occurrence is 2019-10-21 10:45:53
Completed: events process 1 in 0:00:00.009, next occurrence is 2019-10-21 10:46:01
Completed: Flow Engine Event Handler in 0:00:00.006, next occurrence is 2019-10-21 10:45:53
Completed: Flow Engine Event Handler in 0:00:00.006, next occurrence is 2019-10-21 10:45:53
Starting: Layer 2 Connections Creation.aeb1ecca7fa133001952baf8befa91ef, Trigger Type: Interval, Priority: 100, Upgrade Safe: false, Repeat: 10 Seconds
Name: Layer 2 Connections Creation
Job Context: 
#Mon Oct 21 01:45:41 PDT 2019
fcScriptName=in the schedule record

Script: 
GlideEventManager('physical_connections_creation').processDelegatedEvents();
Completed: Layer 2 Connections Creation in 0:00:00.007, next occurrence is 2019-10-21 10:46:01
*** Script: 

REST Response:
Body:
{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "forbidden",
    "message": "Not Authorized to access this resource/api"
   }
  ],
  "code": 403,
  "message": "Not Authorized to access this resource/api"
 }
}

statusCode: 403
*** Script: 3600  null
[0:00:00.596] Total Time


thanks in advance for your time and effort!

 

1 ACCEPTED SOLUTION

Ward van Hoof
Mega Guru

The integration is up and running now. This is my resolution:

1. Ensure the "sub" claim is filled with the email address of the admin

2. if you receive the error "OAuth flow failed. Verify the configurations and try again. Error detail:invalid_scope, Invalid downscoping, scopes should not be specified as a request parameter" re-configure your scopes: 

  • add a custom claim  "scope" to your "JWT Provider" (space separated list of scopes)
  • on the Application Registry record (oauth_entity), add the "Oauth entity scopes" record (same as in claim)
  • make that on the "OAuth entity profile" the related list "OAuth Entity Profile Scopes" is EMPTY

View solution in original post

9 REPLIES 9

In addition to the above..

When I wrote the last post I did not have the correct customer ID, but I did get manage to get the token-request working by removing the sub claim.

However, now having the right customer ID in a HTTP request that consumes that token against the actual Google API resource I do get the same error as you did:

find_real_file.png

But what was a solution for you (adding the sub claim admin user), prevents my logic from getting a token in the first place, so now I am stucked again..

Any ideas?

Anders

So i'm no longer at this client, so it is hard for me to really trouble shoot. THings I had to keep in mind:

your domain is everything after the @ in the email address (so for example to get the group list i gave the parameter "domain=service-now.com")

It says you have a username / password combo. Make sure that the fields client ID & client secret from step 3.4 are blank. (if you don't want to mess with the OOTB record / field states, save the record with a fake ID & secret, and use a background script to erase them.)

I did troubleshooting from python. That way i knew for sure that there is not unexpected logic converting my request. I stripped the service-now sauce, saw everything could work and then rebuild it in service-now.

 See attachment for the python script. I'm not sure whether i used method 1 (commented out in this screenshot) or method 2 in the end. I did all these python scripts from a VM and just had a screenshot of this one. Sadly, i cannot retrace all my troubleshooting steps. (blacked out part in 1 is the full email address (e.g. ward.van.hoof@service-now.com), and the blacked out part in "results" is the domain (e.g. service-now.com)

 

EDIT: one more thing to look at: you can have the endpoint like this /admin/groups/customer/<id>/ etc

or like this /admin/groups?domain=<domain.com>

Thank you again Ward!

I did blank the client ID & client secret fields..

Despite tons of PowerShell experience python doesn't seem intuitive to me, so I will have to look more into it next week.

I will return eventually with some comment to you, so others can learn from our efforts and mistakes.

-Anders

 

Just to finish this...

We managed to get it working when the Service Account was setup correctly following this third party guide. It turned out that the "ENABLE API ACCESS" step in that guide had not been done.

After that I tried populating the Client ID field to see if that would brake it again, but it did still work. I imagine that you while trying to get it working concluded that it had to be blanked (by background script due to it being mandatory), but it does seem that the value in the client ID field does not matter.

I would not have succeeded without your help since I didn't have endless time to succeed in the context of working for a customer.

THANK YOU!

-Anders

Anders Pr_stega
Tera Expert

Thank you Ward, that was very helpful!

For my setup to work the "sub" claim had to be be unspecified, but that might relate to how the Service Account is setup, and since I am doing this for a customer I don't have access and haven't setup the Service Account in the first place - only received informations about it.

I will definitely reference your post. You're a rockstar! 🙂

Sincerely,

Anders