registering callback function for OAuth

Hello there,

I've made an app that uses OAuth to retrieve user data, so I've used registerForOAuthMessages to define a callback function. In the codes hereafter (where the URLs have been obfuscated) I trigger the function "go" when a background event is fired, this function "go" makes the OAuth window pop-up 

using Toybox.Communications as Comm;
using Toybox.System as Sys;
using Toybox.WatchUi as Ui;
using Toybox.Application as App;
using Toybox.Time.Gregorian as Calendar;

const ClientId = "...";
const ClientSecret = "...";
const ApiUrl = "...";
const RedirectUri = "https://localhost/";   

// The LoginTransaction is a special transaction that handles
// getting the OAUTH token.$
(:background)
class LoginTransaction
{
    hidden var _delegate;

    // Constructor
    function initialize(delegate) {
        _delegate = delegate;
        Sys.println("initialize LoginTransaction");
        Comm.registerForOAuthMessages(method(:accessCodeResult));
    }

    // Handle converting the authorization code to the access token
    // @param value Content of JSON response
    function accessCodeResult(value) {
    	Sys.println("accessCodeResult " + value);
        if( value.data != null) {
            Sys.println("accessCodeResult Ok");
        }
        else {
            Sys.println("Error in accessCodeResult");
        }
    }




    // Method to kick off tranaction
    
    function go() {
        // Kick of a request for the user's credentials. This will
        // cause a notification from Connect Mobile to file
        Sys.println("go");
        Comm.makeOAuthRequest(
            // URL for the authorization URL
            "...",//there is a valid URL here but it is private
            // POST parameters
            {},
            // Redirect URL
            $.RedirectUri,
            // Response type
            Comm.OAUTH_RESULT_TYPE_URL,
            // Value to look for
            {"responseCode" => "OAUTH_CODE", "responseError" => "OAUTH_ERROR"}
            );
    }

}

using Toybox.Background as Bg;
using Toybox.System as Sys;

// The Service Delegate is the main entry point for background processes
// our onTemporalEvent() method will get run each time our periodic event
// is triggered by the system.

(:background)
class JsonBackground extends Toybox.System.ServiceDelegate {
	hidden var _transaction=null;
	
	function initialize() {
		Sys.ServiceDelegate.initialize();
		Bg.registerForTemporalEvent(new Time.Duration(5 * 60));
		var ltd=new LoginTransactionDelegate();
	 	_transaction = new LoginTransaction(ltd);

	}
		
    function onTemporalEvent() {
    	System.println("onTemporalEvent2");
        _transaction.go();
        Bg.exit(1);
       
    }
    

}

using Toybox.Background as Bg;
using Toybox.System as Sys;
using Toybox.Communications as Comm;
using Toybox.WatchUi as Ui;

(:background)
class LoginTransactionDelegate extends TransactionDelegate{

    // Constructor
    function initialize() {
        TransactionDelegate.initialize();
    }

    // Handle a error from the server
 	function handleResponse(data) {
       System.println("OK "+data);
    }
    
    function handleError(code) {
        var msg = "err";
        msg += code;
        System.println(msg); 
    }

The OAuth window pop-up correctly when the Background event is triggered, then I enter my credentials correctly and the window close but the "accessCodeResult" function seems not to be called.

Does someone have an idea?
Thanks in advance

  • It wasn't clear that this was a data field or I missed that part. The onInstall and onUpdate functions aren't guaranteed to run at a specific time and may not run in a way the user sees it, not sure this is really an option you can count on. I'm not sure how it would work but you could try on first run of the datafield executing the OAuth if possible but you'd need to check for the phone connection before doing so. I guess you'd need to test it but the OAuth call are asynchronous so the data field could display a notice till the OAuth completes.

  • You're right lcj2 I've not specified in this topic that I dev a data field app, my bad.

    Yes, I plan to write on the screen a waiting message but before doing that I want to succeed in receive data from OAuth

  • I found that here https://www.oreilly.com/library/view/wearable-programming-for/9781492048800/ch04.html
    "If your app closes before the login process completes, the result will be cached on the device until the next time your app calls registerForOAuthMessages, at which point the result will be passed to your callback immediately. "

    So if I was able to call again registerForOAuthMessages or wait untill the closing of OAuth window it will be one way to get around the problem

  • I finally succeed, here is my main App:

    using Toybox.Application as App;
    using Toybox.Background;
    using Toybox.System as Sys;
    using Toybox.WatchUi as Ui;
    using Toybox.Time;
    using Toybox.UserProfile;
    using Toybox.PersistedContent;
    using Toybox.System;
    using Toybox.Communications as Comm;
    
    
    (:background)
    class KronosApp extends App.AppBase {
    	var inBackground=false;
    	var bg=null;
        
        function initialize() {
            AppBase.initialize();
    		Sys.println("AppBase initialize");
        	if(Toybox.System has :ServiceDelegate) {
        		Background.registerForTemporalEvent(new Time.Duration(5 * 60));
        	} else {
        		Sys.println("****background not available on this device****");
       		}
       		Background.registerForOAuthResponseEvent();
       		
          }
    
        // onStart() is called on application start up
        function onStart(state) { 
    	    System.println("start");
        	
        }
    
        // onStop() is called when your application is exiting
        function onStop(state) {
        	if(!inBackground) {
        		Background.deleteTemporalEvent();
        		System.println("stop");
        	}
        	
        }
    
    
        //! Return the initial view of your application here
        function getInitialView() {
        	System.println("getInitialView");
        	var Kview = new KronosView();
            return [ Kview];
        }
        
        //store json data in bgdata
        function onBackgroundData(data) {
    		System.println("onbg "+ data);
        }    
    
        function getServiceDelegate(){
        	inBackground=true;	
        	System.println("getServiceDelegate");
            return [new JsonBackground()];
        }
    
    
    }

    As you can see I've add  Background.registerForOAuthResponseEvent();  in initialize

    And now I've only a JsonBackground  class and no more LoginTransaction class

    using Toybox.Background as Bg;
    using Toybox.Application as App;
    using Toybox.System as Sys;
    using Toybox.Time as Tm;
    using Toybox.Communications as Comm;
    
    // The Service Delegate is the main entry point for background processes
    // our onTemporalEvent() method will get run each time our periodic event
    // is triggered by the system.
    
    (:background)
    class JsonBackground extends Toybox.System.ServiceDelegate {
    	hidden var _transaction=null;
    
    	(:background_method)
    	function initialize() {
    		Sys.ServiceDelegate.initialize();
    	 	_transaction = new LoginTransaction();
    		Comm.registerForOAuthMessages(method(:accessCodeResult));
    	}
    	(:background_method)	
        function onTemporalEvent() {
        	System.println("onTemporalEvent2");
            go();
        }
        
        (:background_method)
        function go() {
            // Kick of a request for the user's credentials. This will
            // cause a notification from Connect Mobile to file
            Sys.println("go");
            Comm.makeOAuthRequest(
                // URL for the authorization URL
                //"https://app.kronos-sport.com/index.php?tg=addon/kronos/main&idx=login.OAuth&response_type=code&client_id="+$.ClientId+"&redirect_uri="+$.RedirectUri+"&scope=&state=azerty",
                "...",
                // POST parameters
                {
                 //.....//	
                },
                // Redirect URL
                $.RedirectUri,
                // Response type
                Comm.OAUTH_RESULT_TYPE_URL,
                // Value to look for
                {"token" => "OAUTH_CODE", "error" => "OAUTH_ERROR"}
                );
             
             
                
        }
        	
        // Handle converting the authorization code to the access token
        // @param value Content of JSON response
        (:background_method)
        function accessCodeResult(response) {
        	Sys.println("accessCodeResult " + response);
            if( response.data != null) {
                //_complete = true;
                // Extract the access code from the JSON response
                Sys.println("accessCodeResult Ok");
                Bg.exit(1);
            }
            else {
                Sys.println("Error in accessCodeResult");
               // _delegate.handleError(value.responseCode);
            }
            
        }
    
    	(:background_method)
        function registerForOAuthResponseEvent(){
    		System.print("Registered for an oauth event"); 
    		return method(:accessCodeResult);
    	}
    	
    }

    Mainly I've add a registerForOAuthResponseEvent function

  • ,

    Why are you annotating your functions with (:background_method)?

    Doing background OAuth is kinda complicated, but the trick, as you have found, is to register for the OAuth response event so that the app will be awakened again when the response is received.

  • Hello travis

    I add (:background_method) because I've found that in a code on the internet

  • Don't trust everything you read on the internet. Slight smile

    Unless you're using it for excludes (it doesn't look like you are), it isn't doing anything. You want to use the (:background) annotation, but it doesn't have to be on every method in a class if it is used on that class.

  • Ok Travis, I don't use background anymore. It was too restrictive