Google OAuth 2.0 Server for Server: Bad Request

I've been banging my head on the wall for three days, trying to get this to work.

POST /oauth2/v3/token HTTP/1.1 Host: www.googleapis.com Content-length: 495 Content-type: application/x-www-form-urlencoded Authorization: Bearer ya29.cgEcY6meBrvaH6oe0nD_PtsFyMVqskiUYi7iJxapKHeEgPoIw8gMt0BJdIvRn1MfcEgzTS3_gTwI1w grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI5MDgyOTgxNjA1NTktc2R1bGFpbWhsaGpxOTY5M2s1Z2E4c25pZjhh%0D%0ANzhlZ3BAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0%0D%0AdHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvYW5hbHl0aWNzLnJlYWRvbmx5%0D%0AIiwiYXVkIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vb2F1dGgyL3Rv%0D%0Aa2VuIiwiZXhwIjoxNDMxNTE0MDUyLCJpYXQiOjE0MzE1MTEwNTJ9.[Cert] HTTP/1.1 400 Bad Request Content-length: 67 X-xss-protection: 1; mode=block X-content-type-options: nosniff Expires: Wed, 13 May 2015 10:08:00 GMT Vary: Origin,X-Origin Server: GSE Cache-control: private, max-age=0 Date: Wed, 13 May 2015 10:08:00 GMT X-frame-options: SAMEORIGIN Content-type: application/json; charset=UTF-8 { "error_description": "Bad Request", "error": "invalid_grant" } 

So, I am creating this in PL / SQL. Oracle 12c. I managed to get the JWT header and the JWT Claim parameter to output the output in the same way as the Google documentation. When I create a certificate, I think a problem has arisen.

  • I need "[----- START A PRIVATE KEY -----" and ----- END PRIVATE KEY ----- \ n] as part of SHA-256 encryption. Do I have to do something with the "new lines"? \ n Should I surround it with brackets?

  • At what point should I be URL encoded?

  • Is the code in the following example sufficient for encryption ?: http://jastraub.blogspot.co.uk/2009/07/hmacsha256-in-plsql.html

  • I connected the function below to find out if you can identify any problems?

Thank you for your help!

  FUNCTION get_JWT (p_token_id ga_app_user.ID_TOKEN%TYPE) RETURN VARCHAR2 IS --Plain text baseJWTheader VARCHAR2 (20000); baseclaimSet VARCHAR2 (20000); baseSigKey VARCHAR2 (20000); --Seconds sysSeconds NUMBER; --Base64 Encoded JWTheader VARCHAR2 (20000); claimSet VARCHAR2 (20000); sigKey VARCHAR2 (20000); sigContent VARCHAR2 (20000); --Returned value output RAW (20000); BEGIN SELECT JWT_HEADER, JWT_CLAIM_SET, SIGNATURE INTO baseJWTheader, baseclaimSet, baseSigKey FROM dwman.ga_app_user au WHERE AU.ID_TOKEN = p_token_id; --DBMS_OUTPUT.PUT_LINE ('Base claim Set ' || baseclaimSet); JWTheader := TRANSLATE ( UTL_RAW.cast_to_varchar2 ( UTL_ENCODE.BASE64_ENCODE (UTL_RAW.CAST_TO_RAW (baseJWTheader))), '+/', '-_'); SELECT ( SYSDATE - TO_DATE ('01-01-1970 00:00:00', 'DD-MM-YYYY HH24:MI:SS')) * 24 * 60 * 60 INTO sysSeconds FROM DUAL; baseclaimSet := REPLACE (baseclaimSet, '#EXPIRE#', ROUND (sysSeconds + 3000)); baseclaimSet := REPLACE (baseclaimSet, '#START#', ROUND (sysSeconds)); --DBMS_OUTPUT.PUT_LINE ('Claim Set ' || baseclaimSet); claimSet := UTL_RAW.cast_to_varchar2 ( UTL_ENCODE.BASE64_ENCODE (UTL_RAW.CAST_TO_RAW (baseclaimSet))); sigKey := baseSigKey; sigContent := JWTheader || '.' || claimSet; --DBMS_OUTPUT.PUT_LINE('Sig Content '||sigContent); sigContent := REPLACE (sigContent, CHR (10), ''); sigContent := REPLACE (sigContent, CHR (13), ''); /* FOR V_TR in 1..length(sigContent) LOOP DBMS_OUTPUT.PUT_LINE (substr(sigContent,V_TR,1)||'='||to_char(ASCII(substr(sigContent,V_TR,1)))); END LOOP; */ sigContent := sigContent || '.' || google_signature (sigContent, sigKey); RETURN UTL_URL.ESCAPE(sigContent, TRUE, 'UTF-8'); END get_JWT; 
+6
source share
1 answer

Your code snippet can only be part of what you did, but it seems that many of the OAUTH steps Google needed to connect were missing.

You can get more information on what these steps are by looking at this URL: https://developers.google.com/accounts/docs/OAuth2WebServer#offline

In the remainder of this answer, I described my own experience by doing something similar (loading data from GA and loading it into the database using SQL statements).

Start by getting customerKey and consumerSecret for your Google project. You will need to have a URL to which Google can redirect, both when requesting your Consumer Key and to supply OAuth to Google during your calls. They must match.

The next step is to send a GET request to Google. Here is an example in C # that can be built using SQL string concatenation.

 String URL_AUTH_FIRST = "https://accounts.google.com/o/oauth2/auth"; String URL_TOKEN_ENDPOINT_SECOND = "https://accounts.google.com/o/oauth2/token"; String url = String.Format( "{0}?client_id={1}&redirect_uri={2}&access_type=offline&scope={3}&response_type=code&state={4}&approval_prompt=force", URL_AUTH_FIRST,_consumerKey_web_app,redir_url,scope,state); 

To do this, you will need a built-in browser. Google.com will redirect this browser to a site under their control so that the user can log in or refuse authorization of your application. After Google receives the necessary information, they redirect back to your built-in browser. You can follow some steps using copy / paste in your browser, but at some point (described below) you should POST return some data that I don’t know if you can do this from the browser application.

Google will respond by redirecting the embedded browser to the URL. The url has data. You need to analyze the parameters of the URL and find the parameter "code". If you get a URL with a β€œcode” as a parameter, you must send the POST format in a specific format back to Google.

 WebClient client = get_WebClient(); // proprietary to include things like proxy info try { NameValueCollection values = new NameValueCollection(); values.Add("client_id", _consumerKey_web_app); values.Add("client_secret", _consumerSecret_web_app_offline); values.Add("grant_type", "authorization_code"); values.Add("redirect_uri", URL_GOOGLE_REDIRECTS_TO_THIS_URL_AFTER_URL_AUTH); values.Add("code", authorization_code); Byte[] responseBytes = client.UploadValues(URL_TOKEN_ENDPOINT_SECOND, values); } 

Google will return "responseBytes", which will be a formatted json string resembling:

 { "access_token":"1/fFAGRNJru1FTz70BzhT3Zg", "expires_in":3920, "token_type":"Bearer", "refresh_token":"1/6BMfW9j53gdGImsixUH6kU5RsR4zwI9lUVX-tqf8JXQ" } 

Access_token is added to your REST API calls.

You can provide a GA request through the REST API, return the data and load it into your database using SQL statements. This is what my application does.

You can save this refresh_token and provide it in future connections. Indeed, all this sequence must be performed using a browser or browser interactively when a user logs on to the system. After that, and you got refresh_token, then your SQL can use and reuse refresh_token basically definitely, at least until the user password changes.

Google will also return error 401 on a regular basis. This means that you need to re-request the access token by sending a new set of values ​​to Google:

 NameValueCollection values = new NameValueCollection(); values.Add("client_id", _consumerKey_web_app); values.Add("client_secret", _consumerSecret_web_app_offline); values.Add("refresh_token", refresh_token); values.Add("grant_type", "refresh_token"); Byte[] responseBytes = client.UploadValues(URL_TOKEN_ENDPOINT_SECOND, values); 
+1
source

Source: https://habr.com/ru/post/987121/


All Articles