Core.api and authorization

  • 1
  • Question
  • Updated 4 years ago
  • Answered
I do not get consistent access to the core api. Sometimes when I "check" authorization I get the 401 error and other times it is fine. Is there a time limit on when you gain access to when you need to re-verify your authentication? Is there a limit on the number of times you can attempt to connect during a certain time period.  Thanks,

"NetworkError: 401 Unauthorized - https://rvaserver2.appspot.com/_ah/api/core/v1/user?username=myemail@blahblah";
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb

Posted 4 years ago

  • 1
Photo of Blake Freeman

Blake Freeman, Official Rep

  • 36,116 Points 20k badge 2x thumb
Glenn,

Thanks for the question. I'll alert one of our developers and perhaps they can shed some light on this for you.
Photo of Alexey

Alexey, Employee

  • 550 Points 500 badge 2x thumb
Glenn,

When you say "when I "check" authorization", what do you mean? Are you talking about your app or Google API explorer?

In your code you can find out when the token expires in your handleAuth function like so:

function handleAuth() {

var request = gapi.client.oauth2.userinfo.get();
request.execute(function(resp) {

if (!resp.code) {
token=gapi.auth.getToken() ;
console.log( “Access Token is now” + token.id_token + “exp ” + token.expires_in );

...

}
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
Hi Alexey,
   This is my application.  I have my environment variable correctly setup (CLIENT_ID) for the google api calls.  I am working on a Ruby/Rails architecture.  When a customer logs into the application, I run javascript to validate my credentials and their email/account information with rise vision database.  Sometimes, I re-check the display status during their session.  I am new to these apis, so the code below is a bit of a hack while I figure it out. 

It works most of the time. I do notice that sometime I get an unauthorized.   Thanks

      checkAuth();   // details below validates my api connection
      initSession('<%= current_user.email %>');  //gets user company information
      getUserStations('<%= current_user.email %>'); //gets users display stations etc... 

/**
 * Validate my credentials 
 */
function checkAuth() {
    console.log('check auth');
    request = gapi.auth.authorize({client_id: CLIENT_ID, scope: SCOPES, immediate: true, cookie_policy: 'single_host_origin'}, handleAuth);
}

/**
 **Load the oauth2 api.
 **/
function handleAuth(authResult) {
    console.log('handle auth');

    if (authResult && !authResult.error) {
        gapi.client.load('oauth2', 'v2', makeOAuth2Request);
    }

}

/**
 * Don't really need this since put userinfo into separate functionality
 */
function makeOAuth2Request() {
    console.log('make request to oauth2');
    var request = gapi.client.oauth2.userinfo.get();


    request.execute(function (resp) {
        if (!resp.code) {
            console.log(resp);
           // not really doing anything
        }
        else {
     //not really doing anything 
        }
    });
}
Photo of Alexey

Alexey, Employee

  • 550 Points 500 badge 2x thumb
Glenn,

Like I mentioned above, in your handleAuth() function you need to get the value of expires_in of your current access token (which you can obtain by calling gapi.auth.getToken()) and based on that re-authorize your app again after X minutes.
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
Hi Alexey,  Appreciate the guidance. That did work. One last questions ;-) I am trying to implement the all the calls in ruby/rails with google_client_api; I am successful at initiating the API client but am unable to discover the apis (see below).  Do you know if its possible to interface with rise vision apis using this technique. The discover_api seems to search the following location: https://developers.google.com/apis-explorer    Thanks!

def userdata
   core = api_client.discovered_api('core', 'v1') /*Error APIs not found*/

   result = api_client.execute(:api_method => core.user.get,
                           :parameters => {'username' => 'myemail@gmail.com'})
                      
        Rails.logger.info "result.data" 

end

private 
 def api_client
   @client ||= begin
     client = Google::APIClient.new(application_name: 'core', application_version: 'v1')
     client.authorization.client_id = ENV['GOOGLE_CLIENT_ID']
     client.authorization.client_secret = ENV["GOOGLE_CLIENT_SECRET"]
     client.authorization.scope = 'https://www.googleapis.com/auth/userinfo.email'
     Rails.logger.info client
     client
   end

  
Hi Glenn,

I am not too familiar with Ruby, but I had look at the google-api-ruby-client documentation and it seems you are missing an instantiation of a InstalledAppFlow as showed on the following code snippet.

# Run installed application flow. Check the samples for a more
# complete example that saves the credentials between runs.
flow = Google::APIClient::InstalledAppFlow.new(
  :client_id => client_secrets.client_id,
  :client_secret => client_secrets.client_secret,
  :scope => ['https://www.googleapis.com/auth/plus.me']
)
client.authorization = flow.authorize

Let me know if you need further help.

Cheers!
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
Hi Rodrigo, Thank you for the reply. I implemented the InstalledAppFlow; but still don't succeed in trying to discover the apis --  "core = api_client.discovered_api('core', 'v1')" --   In javascript this is accomplished with the gapi.client.load that has a variable (i.e. ROOT) that points to the apis. 'https://rvaserver2.appspot.com/_ah/api  I guess I am wondering if this is possible with this ruby technique.  I will continue to work it; but let me know if you have any ideas ;-)  And I will certainly update if I succeed :-)

//Ruby function points to the Google API explorer hub
core = api_client.discovered_api('core')


//JAVASCRIPT ROOT points to https://rvaserver2.appspot.com/_ah/api'
 gapi.client.load(API_NAME, API_VER, function () {
        var request = gapi.client.core.user.get(parameters);

        request.execute(function (jsonResp, rawResp) {

        });
    }, ROOT);
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
It looks like the api_client.discovered_api(name, version) references the following location:  https://www.googleapis.com/discovery/v1/apis  (JSON index of available apis). Details of these apis are located here...  https://developers.google.com/apis-explorer/#p/  I was successful accessing api listed in this index.   I am still unsure how I can reference the rise vision core apis (not listed in this index).
For example, the discovery json index has "bigquery". And the ruby function client.discovered_api('bigquery', 'v2') returns access to those API calls. Is there a way of getting "core" added to this list to promote the rise vision access points in ruby :-)  Or is there another way to point the rise api to the ravcore location:  https://apis-explorer.appspot.com/apis-explorer/?base=https://rvacore-test.appspot.com/_ah/api#p/dis...   I have the javascript working; but would be much cleaner in ruby for this particular project.  Thanks

discovery JSON index for bigquery:
{
   "kind": "discovery#directoryItem",
   "id": "bigquery:v2",
   "name": "bigquery",
   "version": "v2",
   "title": "BigQuery API",
   "description": "A data platform for customers to create, manage, share and query data.",
   "discoveryRestUrl": "https://www.googleapis.com/discovery/v1/apis/bigquery/v2/rest",
   "discoveryLink": "./apis/bigquery/v2/rest",
   "icons": {
    "x16": "https://www.google.com/images/icons/product/search-16.gif",
    "x32": "https://www.google.com/images/icons/product/search-32.gif"
   },
   "documentationLink": "https://developers.google.com/bigquery/docs/overview",
   "preferred": true
  },

 
Hi Glenn,

I understand your problem with the ruby client lib so I would suggest you to contact the developers of the library on github to see if the library works with non-google apis. If I am not wrong the discovery/v1/apis only list google apis. 

Also, I could not find anything on Google endpoints documentation regarding to use ruby as client. They talk about Java, Javascript, Android and IOS clients.
https://cloud.google.com/appengine/docs/java/endpoints/

To help us accessing out Core api with javascript we have developed some angularJs modules and they are hosted on github. I am not sure if that helps you but in any case here are the links to them:

https://github.com/Rise-Vision/ng-gapi-loader
https://github.com/Rise-Vision/ng-core-api-client
    
 
Thanks a mill!
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
Hey Rodrigo!  I am in touch with the github developer and we working through it.  It looks like I was on the right track with creating a JSON file index of the rise api and feeding or registering with the google-ruby discovery api call; a few hiccups on the authentication.  I needed to attend to business side of the house today; but should have this working at the next coding session ;-) I will post the working snippets. Thanks again for the replies. 
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb

Here is an example of implementing google-api-ruby-client: https://github.com/google/google-api-ruby-client This is working for all methods except "core.display". 


(@client.register_discovery_uri('core', 'v1', 'https://rvaserver2.appspot.com/')) rescue Exception

#the core.json should be the json index for the custom api. 
@core = @client.register_discovery_document('core', 'v1', File.read("#{Rails.root}/tmp/core.json"))

#call the apis 
result = @client.execute(:api_method => @core.user.get, :parameters => {'username' => @user.email })

  *****All the methods work except 'core.display' ******  
The library is ignoring all the display resources/methods (i.e. core.display.get, core.display.list) for the api because they conflict with that libraries built-in methods on the object.  I have filed an issue on Github but another option would be to change the API definition for "display" and renaming the resource to something like videodisplay. That would fix the problem.


Photo of Alexey

Alexey, Employee

  • 550 Points 500 badge 2x thumb
Glenn,

Is it possible to change the library you are using or use a different library?

We can't really go renaming API methods because there are other developers who are also using this API.

Regards,
Alexey
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
Hi Alexey,  Agree. I was kind of surprised by that suggestion from the developer.  I am able to get around it by changing the definition in the core.json file stored with the application and use to map out the rise core api. (i.e. videodisplay). And it works like a charm. When you reference the api using the google client, you reference it based on that new definition.  

example core.videodisplay.get etc...  If anyone ever has an issue with this, happy to help :-)

Thanks,
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
Hi Alexey or anyone else...  me again ;-) I am running the presentation patch command and getting an error. It seems to indicate the JSON is not properly formatted, but think it is.  Your thoughts greatly appreciated. 

#the function
@client.execute(:api_method => @core.presentation.patch, :parameters => {'id' => _presoId, 'data' => updateFields })

#updateFields inputs attempted:
{"name"=>"TestName-2"}
{"name": "TestName-2"}

ERROR:  "message": "com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: Can not instantiate value of type [map type; class java.util.LinkedHashMap, [simple type, class java.lang.String] -\u003e [simple type, class java.lang.Object]] from JSON String; no single-String constructor/factory method (through reference chain: com.risevision.core.api.v1.EndpointRequestBody[\"data\"])"


thanks, 

Photo of Alexey

Alexey, Employee

  • 550 Points 500 badge 2x thumb
Glenn,

I am having trouble finding this error in our server logs.

I would appreciate if you could provide some more information, such as approximate time you got the error, presentation ID, etc.


Regards,
Alexey
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
Hi Alexey.  I just retried it and got the error. Here are some details. thanks

 "item": {
 "id": "0701c557-19ce-47dd-8948-ee0aca51d1cf",
 "companyId": "04cd5409-4e1a-401a-a505-c82332f2e700", 

Photo of Alexey

Alexey, Employee

  • 550 Points 500 badge 2x thumb
Glenn,

I see the errors now but from what I can tell Google Endpoints return 400 (bad request) before my code even runs which tells me that Endpoints may have trouble parsing your call.

Perhaps your JSON is not formatted correctly?

Does it work if you try the same API call in Google API explorer?

Do you wrap your parameters in "data" object like so: 

PATCH https://rvacore-test.appspot.com/_ah/api/core/v1/presentation?id=xxx-xxx-xxx
Content-Type:  application/json
Authorization:  Bearer xxxxxxxxxxxxxxxx
X-JavaScript-User-Agent:  Google APIs Explorer
{
"data": {
"name": "new name"
}
}
?
(Edited)
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
Alexey,  It does work via the api-explorer.  And i have it working in another backbone/node.js javascript project (using gapi). Its just this outdated ruby stuff ;-) I think it might be related to the core.json file needed to initialize these calls.  thanks

Does the below look correct from the core.json? 

"patch": {     "id": "core.presentation.patch",
     "path": "presentation",
     "httpMethod": "PATCH",
     "parameters": {
      "id": {
       "type": "string",
       "required": true,
       "location": "query"
      }
     },
     "parameterOrder": [
      "id"
     ],
     "request": {
      "$ref": "EndpointRequestBody",
      "parameterName": "resource"
     },
     "response": {
      "$ref": "APIResponse_PresentationWrapper"
     },
     "scopes": [
      "https://www.googleapis.com/auth/userinfo.email";
     ]
    }
Photo of Glenn Reese

Glenn Reese

  • 838 Points 500 badge 2x thumb
Hi Alexey,  I got this working. Below is the call structure using google ruby client api.  

 result = @client.execute(:api_method => @core.presentation.patch,            :parameters => {'id' => _pId },
 :body => jsonObj, 
 :headers => {'Content-Type'=> 'application/json'}

Thanks again for the guidance.  -Glenn