简体   繁体   中英

OAuth with Ionic 3 and Rails (devise_token_auth)

I'm building an Ionic 3 (Angular 4) app with a Ruby on Rails backend.

I'm using angular2-token and devise_token_auth for user and API calls authentication, which are meant to be used together.

The way they work is simple: they create and keep track of some fields (namely auth-token , client , uid and expiry ) on every API call headers.

It's working flawlessly with email signup/login. I now want to implement Instagram OAuth login for my app, which should be a no brainer in a regular Angular web app, but here in Ionic I don't have control over routing or URLs, so the strategy to go is to launch an InAppBrowser and set events on it to detect when it reaches my API callback URL.

Basically:

  1. I load myapi.com/auth/instagram inside an InAppBrowser
  2. Instagram login, redirects to callback URL
  3. InAppBrowser loads myapi.com/omniauth/instagram/callback?code=57... and I detect it.

At this point, my backend has successfully created a user based on the Instagram data.

The problem is, I have no idea how to handle this on the client side.

When I login with email/password, what angular2-token does is POST /auth/sign_in and the request headers contain the auth_token and stuff and then handles it and stores it for subsequent calls. Now the OAuth flow lives in an InAppBrowser instance, out of my control...

I'm really frustrated since I see the OAuth flow working on the backend side and the frontend part could be easily done on a regular web app outside that InAppBrowser.

I'll copy some of my frontend and backend code to give more context, hope that's helpful for understanding the problem

Frontend that handles the OAuth flow:

  public logInWithInstagram(): Promise<any>{
    var callbackURL = environment.token_auth_config.apiBase + "/omniauth/instagram/callback";
    var oauthURL = environment.token_auth_config.apiBase + "/auth/instagram";
    var parsedResponse = {};

    return new Promise(function(resolve, reject) {
      var browserRef = window.cordova.InAppBrowser.open(oauthURL, "_blank", "location=no,clearsessioncache=yes,clearcache=yes");
      browserRef.addEventListener("loadstart", (event) => {
        if ((event.url).indexOf(callbackURL) === 0) {
          browserRef.removeEventListener("exit", (event) => {});
          browserRef.close();
          var responseParameters = ((event.url).split("?")[1]).split("&");
          var parsedResponse = {};
          for (var i = 0; i < responseParameters.length; i++) {
            parsedResponse[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
          }
          if (typeof(parsedResponse["code"]) !== 'undefined' && parsedResponse["code"] !== null) {
            // Successful Instagram OAuth, backend has already created the user by now
            resolve(parsedResponse);
          } else {
            reject("Problem authenticating");
          }
        }
      });
      browserRef.addEventListener("exit", function(event) {
        reject("The sign in flow was canceled");
      });
    });
  }

Backend, the part that creates the user and handles the callback URL:

class Users::OmniauthCallbacksController < DeviseTokenAuth::OmniauthCallbacksController

  def redirect_callbacks    
    @user = User.from_omniauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user
    else
      session["devise.instagram_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end

  end

  def failure
    redirect_to root_path
  end
end

I would really appreciate any help, strategies or ideas. Thank you very much!

Angular2-token is in alpha, and doesn't handle the credentials returned to inAppBrowser yet.

We ended up forking the project and adding a branch of logic in the angular2-token package to handle the problem.

It's still a little janky (eg oAuthLogin doesn't return an observable yet, you have to poll for login success), but it works.

The package is up here if you want to try it out: https://github.com/chadnaylor/angular2-token-ionic3

It's essentially identical to angular2-token except for the ionic logic. Hopefully we can merge it in once I've cleaned it up a bit.

If you want to try tweaking it yourself, the bit we changed looks like:

browser.on('loadstop').subscribe((ev: InAppBrowserEvent) => {
        if (0 === ev.url.indexOf(this.atOptions.oAuthBrowserCallback)) {
          browser.executeScript({code: "requestCredentials();"})
            .then((credentials) => {
              // alert(JSON.stringify(credentials[0]));
              this.getAuthDataFromPostMessage(credentials[0]);

              let pollerObserv = Observable.interval(400);

              let pollerSubscription = pollerObserv.subscribe(() => {
                if(this.userSignedIn()){
                  pollerSubscription.unsubscribe();
                  browser.close();
                }
              });

            })
        }

      });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM