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:
myapi.com/auth/instagram
inside an InAppBrowser
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.