{"activeVersionTag":"latest","latestAvailableVersionTag":"latest","collection":{"info":{"_postman_id":"53ba65c6-b093-4b3d-972d-4e6872d77c4a","name":"Google API GSuite - Single Scope","description":"# Postman, GoogleAPI & OAuth2\n\n### a.k.a. The good, the bad, and the ugly. It's up to you how you order the components!\n\n## Summary\nComing from a DevSecOps background I decided that I ought to maintain my infrastructure as code.  \n&nbsp;  \nTo achieve this for [G Suite](https://gsuite.google.com/) one has to utilise the [G Suite GoogleAPI (gAPI)](https://developers.google.com/admin-sdk) which in turn requires [OAuth2](https://aaronparecki.com/oauth-2-simplified/).  \n\nThe fun, not to mention the bun fight, begins...  \n&nbsp;  \nWhen using OAuth2 with [Postman](https://www.postman.com/) for our personal accounts we really only need 2-legged OAuth but we have to use 3-legged OAuth with gAPI (see the [OAuth2](https://aaronparecki.com/oauth-2-simplified/) link if you need to understand 2 verus 3 legs).  The third leg has to be performed \nmanually (although I'm still searching for a solution to that; suggestions welcome).  \n&nbsp;  \nAs I struggled to get this working I thought I was just being dim witted but found that I had actually followed in the hallowed footsteps of no less a deity than [Martin Fowler](https://martinfowler.com/articles/command-line-google.html).  Even his article is now out of date after just nine months thanks to changes in gAPI.  \n&nbsp;  \nPostman does have a method that automates the process but gAPI tokens only last one hour so you have to manually refresh the token.  \n&nbsp;  \n**This collection** is for use with a single scope only.  Multiple scopes should work but have failed to do so during my efforts.  In theory you should use the minimum privilege possible so using a collection per scope is no bad thing. However, I am creating a multi-scope collection that will allow all the requests for G Suite administration in one collection.  \n&nbsp;  \n**The conclusion** I came to is that you cannot use Postman, as great a tool that it is, to fully automate this process.  You are highly recommended to use a full language such as Python, Go, Javascript, [insert your language preference here].  Whichever language you choose, you'll still need to understand these steps first.  \n&nbsp;   \nThe process required is documented below. Steps 1-4 have to be completed manually but, hopefully, only once.  \nStep 5 shows how to refresh the `access_token` manually.  The collection uses `pre-request-scripts` to perform this task automatically only when the token has expired.\n\n## Working Steps Version 1\n\n**1) In browser we get a !ONE TIME! CODE :**\n\nG Suite Admin is undertaken via the [Google Cloud Platform console](https://console.cloud.google.com).  You can setup credentials and scopes at the [APIs & Services credentials](https://console.cloud.google.com/apis/credentials?project=gsuite-itp-com) tab.  This will give you the **client_id** used below.\n   \n  - Single scope - you can have an access_code per scope\n\n  ```\n  https://accounts.google.com/signin/oauth?scope=https://www.googleapis.com/auth/admin.directory.group&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=744399Sani-tisedio43fagg4amsaonind68spa45nk.apps.googleusercontent.com&o2v=1&as=ejH4UFCyT0CRlUh768SBow\n  ```\n  Change the `scope` to the required access.  Checkout \n  [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes)\n  to find the appropriate scope  \n&nbsp;  \n\n  - Multiple scopes in theory !!! doesn't work !!! Suggestions welcome\n\n  ```\n  https://accounts.google.com/signin/oauth?scope=\"https://www.googleapis.com/auth/admin.directory.group https://www.googleapis.com/auth/admin.directory.domain\"&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=744399Sani-tisedo43fagg4amsaonind68spa45nk.apps.googleusercontent.com&o2v=1&as=ejH4UFCyT0CRlUh768SBow\n  ```\n\n  - **NOTA BENE:** We should only need to get this once and it is seemingly \n  impossible to automate using the GoogleAPI. Thus it is a manual step.\n  Once we have a `refresh_token` (see step 4) then we can use that \n  programmatically to update the `access_code` when it expires.  \n&nbsp;  \n\n**2) Copy code from browser screen and export:**\n\n  ```\n  export CODE=\"4/1AFMOCUiVByuzjSanitisedqdpdVsdQJm8IcqhUl_GxPkdJycMX8Vo\"\n  ```\n&nbsp;  \n\n**3) Export other variables:**\n\n  ```\n  export CLIENT_SECRET=\"jZ9AWSanitisedQvXHG\"                                              # Obtained from https://console.developers.google.com/apis/credentials\n  export CLIENT_ID=\"744399Sani-tisedio43fagg4amsaonind68spa45nk.apps.googleusercontent.com\"  # Obtained from https://console.developers.google.com/apis/credentials\n  export SCOPE=\"https://www.googleapis.com/auth/admin.directory.group https://www.googleapis.com/auth/admin.directory.domain https://www.googleapis.com/auth/admin.directory.customer https://www.googleapis.com/auth/admin.directory.group.member https://www.googleapis.com/auth/admin.directory.orgunit https://www.googleapis.com/auth/admin.directory.rolemanagement https://www.googleapis.com/auth/admin.directory.user https://www.googleapis.com/auth/admin.directory.user.alias https://www.googleapis.com/auth/admin.directory.user.security https://www.googleapis.com/auth/admin.directory.userschema https://www.googleapis.com/auth/cloud-platform\"\n  ```\n  - Scope obtained from https://developers.google.com/identity/protocols/oauth2/scopes\n  \n&nbsp;  \n\n**4) Run the following curl command:**\n\n```\ncurl -s -X POST \\\n  -d code=$CODE \\\n  -d client_id=$CLIENT_ID \\\n  -d client_secret=$CLIENT_SECRET \\\n  -d redirect_uri=urn:ietf:wg:oauth:2.0:oob \\\n  -d grant_type=authorization_code \\\n  -d access_type=offline \\\n  -L https://accounts.google.com/o/oauth2/token\n```\n\n  This will respond with:\n```\n{\n  \"access_token\": \"ya29.a0AfH6SMAsLAfqnBDish0KGVzxV4gVjU1XgofQQRnaE8mNV4S9djg7RMbC4BgYEv23ENBQXH8YsXL3pr8Qy0yKSImJc1yAi6wXukQkZrvEEHanDmxxcjpyglg_KmaY4HXgWk0nccvhf8NrBqWi5JzSVoo-ev_50d4Z7Lo\",\n  \"expires_in\": 3599,\n  \"refresh_token\": \"1//03omSg5YlxYZnCgRAAG-you're-nuts-if-you-think-I-haven't-sanitised-this-J6scO7bNzJwhKqh3f2mI_gFVqu9bQ\",\n  \"scope\": \"https://www.googleapis.com/auth/admin.directory.domain\",\n  \"token_type\": \"Bearer\"\n}\n```\n\nThe access_token is required to run individual API calls. \nIn Postman, set the Authorisation to OAuth2 and pass the \n`access_token` from an environment variable e.g. \n`{{access_token}}` you can have an access_code per scope \ne.g. `{{access_token_domain}}` and `{{access_token_group}}` but \nyou have to refresh each one with it's own access_code\n\n&nbsp;  \n\n**5) Refresh the access_code**\n\nThe access_code expires after 3599 seconds (one hour) and needs to be refreshed:\n\n  - Export the refresh_token obtained from step (4)\n\n  ```\n  export REFRESH_TOKEN=\"1//03omSg5YlxYZnCgYIARAAG-you're-nuts-if-you-think-I-haven't-sanitised-this-F-flr2jVyZQ7HuJ6scO7bNzJwhKqh3f2mI_gFVqu9bQ\"\n  ```\n\n  - Run the following curl command:\n\n```\ncurl -s -X POST \\\n -d refresh_token=$REFRESH_TOKEN \\\n -d client_id=$CLIENT_ID \\\n -d client_secret=$CLIENT_SECRET \\\n -d redirect_uri=urn:ietf:wg:oauth:2.0:oob \\\n -d grant_type=refresh_token \\\n -d access_type=offline \\\n -L https://accounts.google.com/o/oauth2/token\n```\n\n  - This will respond with:\n```\n{\n  \"access_token\": \"ya29.a0AfH6SMDEHgHzDuTDGLH9QcRW7JQzskPDmZsanatised_aumT00r36VfBYc6nxlfPLhcn-SyMAp02SwQtL6P3evjZOKkG8dsjerrJds0j0CeouOI43qxA-dkTFuSnPloGIj2nAz3tL1gVJK2HMGqM70b0\",\n  \"expires_in\": 3599,\n  \"scope\": \"https://www.googleapis.com/auth/admin.directory.domain\",\n  \"token_type\": \"Bearer\"\n}\n```\n\n&nbsp;\n\n## Useful Links\n\nhttps://developers.google.com/google-ads/api/docs/concepts/call-structure\n\nhttps://stackoverflow.com/questions/32076503/using-postman-to-access-oauth-2-0-google-apis\n\nhttps://developers.google.com/oauthplayground\n\nhttps://developers.google.com/people/\n\nhttps://developers.google.com/identity/sign-in/web/devconsole-project\n\nhttps://developers.google.com/admin-sdk\n\n\n### Google API Scope \nThis is space separated in Postman, but comma separated in Google:\n\nhttps://www.googleapis.com/auth/admin.directory.group\n\nhttps://www.googleapis.com/auth/admin.directory.domain","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","isPublicCollection":true,"owner":"5221394","team":1131902,"collectionId":"53ba65c6-b093-4b3d-972d-4e6872d77c4a","publishedId":"SzzobbXE","public":true,"publicUrl":"https://documenter-api.postman.tech/view/5221394/SzzobbXE","privateUrl":"https://go.postman.co/documentation/5221394-53ba65c6-b093-4b3d-972d-4e6872d77c4a","customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"EF5B25"},"documentationLayout":"classic-double-column","customisation":null,"version":"8.10.0","publishDate":"2020-06-23T00:28:39.000Z","activeVersionTag":"latest","documentationTheme":"light","metaTags":{},"logos":{}},"statusCode":200},"environments":[{"name":"GSuite gAPI Domain Scope","id":"c68e3f1b-a784-4dea-acc6-1b76cb451df9","owner":"5221394","values":[{"key":"access_code","value":null,"enabled":true},{"key":"access_code_timestamp","value":"2020-08-23T15:55:52.538Z","enabled":true},{"key":"access_code_expiry_time","value":3599000,"enabled":true},{"key":"Refresh_Token","value":"Paste Refresh Token Here","enabled":true},{"key":"clientID","value":"Paste client_Id Here","enabled":true},{"key":"clientSecret","value":"Paste client_Secret Here","enabled":true},{"key":"CallBackURL","value":"urn:ietf:wg:oauth:2.0:oob","enabled":true},{"key":"customerID","value":"Paste customer_Id Here","enabled":true},{"key":"domainName","value":"example.com","enabled":true},{"key":"domainAlias","value":"example.fun","enabled":true},{"key":"newDomainName","value":"newexample.com","enabled":true},{"key":"gAPI_Version","value":"v1","enabled":true},{"key":"DELETEDomainName","value":"newexample.com","enabled":true},{"key":"HiddenHash","value":"a76a2c6059db29d67e6ee4b838b9324373cdf41514f65c9cd264b797ab4618eb882535ba81495ea89be1621381b5328a5ad7efec555a2930b65a352228f8ce71","enabled":true}],"published":true}],"user":{"authenticated":false,"permissions":{"publish":false}},"run":{"button":{"js":"https://run.pstmn.io/button.js","css":"https://run.pstmn.io/button.css"}},"web":"https://www.getpostman.com/","team":{"logo":"https://res.cloudinary.com/postman/image/upload/t_team_logo_pubdoc/v1/team/a6faa797beab03ff8116d3548e26f68ae95784957fa2c1bda21bef89c4ce1c4f","favicon":""},"isEnvFetchError":false,"languages":"[{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"HttpClient\"},{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"RestSharp\"},{\"key\":\"curl\",\"label\":\"cURL\",\"variant\":\"cURL\"},{\"key\":\"dart\",\"label\":\"Dart\",\"variant\":\"http\"},{\"key\":\"go\",\"label\":\"Go\",\"variant\":\"Native\"},{\"key\":\"http\",\"label\":\"HTTP\",\"variant\":\"HTTP\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"OkHttp\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"Unirest\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"Fetch\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"jQuery\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"XHR\"},{\"key\":\"c\",\"label\":\"C\",\"variant\":\"libcurl\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Axios\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Native\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Request\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Unirest\"},{\"key\":\"objective-c\",\"label\":\"Objective-C\",\"variant\":\"NSURLSession\"},{\"key\":\"ocaml\",\"label\":\"OCaml\",\"variant\":\"Cohttp\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"cURL\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"Guzzle\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"HTTP_Request2\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"pecl_http\"},{\"key\":\"powershell\",\"label\":\"PowerShell\",\"variant\":\"RestMethod\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"http.client\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"Requests\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"httr\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"RCurl\"},{\"key\":\"ruby\",\"label\":\"Ruby\",\"variant\":\"Net::HTTP\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"Httpie\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"wget\"},{\"key\":\"swift\",\"label\":\"Swift\",\"variant\":\"URLSession\"}]","languageSettings":[{"key":"csharp","label":"C#","variant":"HttpClient"},{"key":"csharp","label":"C#","variant":"RestSharp"},{"key":"curl","label":"cURL","variant":"cURL"},{"key":"dart","label":"Dart","variant":"http"},{"key":"go","label":"Go","variant":"Native"},{"key":"http","label":"HTTP","variant":"HTTP"},{"key":"java","label":"Java","variant":"OkHttp"},{"key":"java","label":"Java","variant":"Unirest"},{"key":"javascript","label":"JavaScript","variant":"Fetch"},{"key":"javascript","label":"JavaScript","variant":"jQuery"},{"key":"javascript","label":"JavaScript","variant":"XHR"},{"key":"c","label":"C","variant":"libcurl"},{"key":"nodejs","label":"NodeJs","variant":"Axios"},{"key":"nodejs","label":"NodeJs","variant":"Native"},{"key":"nodejs","label":"NodeJs","variant":"Request"},{"key":"nodejs","label":"NodeJs","variant":"Unirest"},{"key":"objective-c","label":"Objective-C","variant":"NSURLSession"},{"key":"ocaml","label":"OCaml","variant":"Cohttp"},{"key":"php","label":"PHP","variant":"cURL"},{"key":"php","label":"PHP","variant":"Guzzle"},{"key":"php","label":"PHP","variant":"HTTP_Request2"},{"key":"php","label":"PHP","variant":"pecl_http"},{"key":"powershell","label":"PowerShell","variant":"RestMethod"},{"key":"python","label":"Python","variant":"http.client"},{"key":"python","label":"Python","variant":"Requests"},{"key":"r","label":"R","variant":"httr"},{"key":"r","label":"R","variant":"RCurl"},{"key":"ruby","label":"Ruby","variant":"Net::HTTP"},{"key":"shell","label":"Shell","variant":"Httpie"},{"key":"shell","label":"Shell","variant":"wget"},{"key":"swift","label":"Swift","variant":"URLSession"}],"languageOptions":[{"label":"C# - HttpClient","value":"csharp - HttpClient - C#"},{"label":"C# - RestSharp","value":"csharp - RestSharp - C#"},{"label":"cURL - cURL","value":"curl - cURL - cURL"},{"label":"Dart - http","value":"dart - http - Dart"},{"label":"Go - Native","value":"go - Native - Go"},{"label":"HTTP - HTTP","value":"http - HTTP - HTTP"},{"label":"Java - OkHttp","value":"java - OkHttp - Java"},{"label":"Java - Unirest","value":"java - Unirest - Java"},{"label":"JavaScript - Fetch","value":"javascript - Fetch - JavaScript"},{"label":"JavaScript - jQuery","value":"javascript - jQuery - JavaScript"},{"label":"JavaScript - XHR","value":"javascript - XHR - JavaScript"},{"label":"C - libcurl","value":"c - libcurl - C"},{"label":"NodeJs - Axios","value":"nodejs - Axios - NodeJs"},{"label":"NodeJs - Native","value":"nodejs - Native - NodeJs"},{"label":"NodeJs - Request","value":"nodejs - Request - NodeJs"},{"label":"NodeJs - Unirest","value":"nodejs - Unirest - NodeJs"},{"label":"Objective-C - NSURLSession","value":"objective-c - NSURLSession - Objective-C"},{"label":"OCaml - Cohttp","value":"ocaml - Cohttp - OCaml"},{"label":"PHP - cURL","value":"php - cURL - PHP"},{"label":"PHP - Guzzle","value":"php - Guzzle - PHP"},{"label":"PHP - HTTP_Request2","value":"php - HTTP_Request2 - PHP"},{"label":"PHP - pecl_http","value":"php - pecl_http - PHP"},{"label":"PowerShell - RestMethod","value":"powershell - RestMethod - PowerShell"},{"label":"Python - http.client","value":"python - http.client - Python"},{"label":"Python - Requests","value":"python - Requests - Python"},{"label":"R - httr","value":"r - httr - R"},{"label":"R - RCurl","value":"r - RCurl - R"},{"label":"Ruby - Net::HTTP","value":"ruby - Net::HTTP - Ruby"},{"label":"Shell - Httpie","value":"shell - Httpie - Shell"},{"label":"Shell - wget","value":"shell - wget - Shell"},{"label":"Swift - URLSession","value":"swift - URLSession - Swift"}],"layoutOptions":[{"value":"classic-single-column","label":"Single Column"},{"value":"classic-double-column","label":"Double Column"}],"versionOptions":[],"environmentOptions":[{"value":"0","label":"No Environment"},{"label":"GSuite gAPI Domain Scope","value":"5221394-c68e3f1b-a784-4dea-acc6-1b76cb451df9"}],"canonicalUrl":"https://documenter.gw.postman.com/view/metadata/SzzobbXE"}