Sto seguendo questo articolo su accessi sociali con AngularJS e ASP.Net WebAPI (che è abbastanza buona):AngularJS e ASP.Net WebAPI sociale di login su un mobile browser
ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app
Praticamente, il il codice funziona correttamente quando si esegue l'accesso social tramite un browser desktop (es. Chrome, FF, IE, Edge). L'accesso social si apre in una nuova finestra (non nella scheda) e puoi utilizzare il tuo account Google o Facebook e una volta che hai effettuato l'accesso tramite uno di essi, verrai reindirizzato alla pagina di richiamata (authComplete.html), e la pagina di callback ha un file JS definito (authComplete.js) che chiude la finestra ed esegue un comando sulla finestra padre.
il controllore angularJS che chiama l'url di accesso esterno e apre una finestra pop-up (non scheda) sul browser desktop:
loginController.js
'use strict';
app.controller('loginController', ['$scope', '$location', 'authService', 'ngAuthSettings', function ($scope, $location, authService, ngAuthSettings) {
$scope.loginData = {
userName: "",
password: "",
useRefreshTokens: false
};
$scope.message = "";
$scope.login = function() {
authService.login($scope.loginData).then(function (response) {
$location.path('/orders');
},
function (err) {
$scope.message = err.error_description;
});
};
$scope.authExternalProvider = function (provider) {
var redirectUri = location.protocol + '//' + location.host + '/authcomplete.html';
var externalProviderUrl = ngAuthSettings.apiServiceBaseUri + "api/Account/ExternalLogin?provider=" + provider
+ "&response_type=token&client_id=" + ngAuthSettings.clientId
+ "&redirect_uri=" + redirectUri;
window.$windowScope = $scope;
var oauthWindow = window.open(externalProviderUrl, "Authenticate Account", "location=0,status=0,width=600,height=750");
};
$scope.authCompletedCB = function (fragment) {
$scope.$apply(function() {
if (fragment.haslocalaccount == 'False') {
authService.logOut();
authService.externalAuthData = {
provider: fragment.provider,
userName: fragment.external_user_name,
externalAccessToken: fragment.external_access_token
};
$location.path('/associate');
}
else {
//Obtain access token and redirect to orders
var externalData = { provider: fragment.provider, externalAccessToken: fragment.external_access_token };
authService.obtainAccessToken(externalData).then(function (response) {
$location.path('/orders');
},
function (err) {
$scope.message = err.error_description;
});
}
});
}
}]);
authComplete.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<script src="scripts/authComplete.js"></script>
</body>
</html>
authComplete.js
window.common = (function() {
var common = {};
common.getFragment = function getFragment() {
if (window.location.hash.indexOf("#") === 0) {
return parseQueryString(window.location.hash.substr(1));
} else {
return {};
}
};
function parseQueryString(queryString) {
var data = {},
pairs, pair, separatorIndex, escapedKey, escapedValue, key, value;
if (queryString === null) {
return data;
}
pairs = queryString.split("&");
for (var i = 0; i < pairs.length; i++) {
pair = pairs[i];
separatorIndex = pair.indexOf("=");
if (separatorIndex === -1) {
escapedKey = pair;
escapedValue = null;
} else {
escapedKey = pair.substr(0, separatorIndex);
escapedValue = pair.substr(separatorIndex + 1);
}
key = decodeURIComponent(escapedKey);
value = decodeURIComponent(escapedValue);
data[key] = value;
}
return data;
}
return common;
})();
var fragment = common.getFragment();
window.location.hash = fragment.state || '';
window.opener.$windowScope.authCompletedCB(fragment);
window.close();
Il problema che sto avendo è che quando faccio funzionare l'applicazione su un dispositivo mobile (Safari, Chrome per dispositivi mobili), la finestra di login sociale, si apre in una nuova scheda e la funzione JS, che aveva lo scopo di passare indietro il frammento nella finestra principale dell'applicazione non viene eseguito e la nuova scheda non si chiude.
Si può effettivamente provare questo comportamento sia su un desktop e browser mobile attraverso l'applicazione:
http://ngauthenticationapi.azurewebsites.net/
Quello che ho provato finora in questo contesto è nel controller di accesso, ho modificato la funzione in modo che l'URL di accesso esterna si apre nella stessa finestra:
$scope.authExternalProvider = function (provider) {
var redirectUri = location.protocol + '//' + location.host + '/authcomplete.html';
var externalProviderUrl = ngAuthSettings.apiServiceBaseUri + "api/Account/ExternalLogin?provider=" + provider
+ "&response_type=token&client_id=" + ngAuthSettings.clientId
+ "&redirect_uri=" + redirectUri;
window.location = externalProviderUrl;
};
E modificato la funzione authComplete.js common.getFragment per tornare alla pagina di login, aggiungendo il token di accesso fornito dal sociali login come stringa di query:
common.getFragment = function getFragment() {
if (window.location.hash.indexOf("#") === 0) {
var hash = window.location.hash.substr(1);
var redirectUrl = location.protocol + '//' + location.host + '/#/login?ext=' + hash;
window.location = redirectUrl;
} else {
return {};
}
};
E nel controller di accesso, ho aggiunto una funzione per analizzare il querystring e provare a chiamare la funzione $ scope.authCompletedCB (frammento) come:
var vm = this;
var fragment = null;
vm.testFn = function (fragment) {
$scope.$apply(function() {
if (fragment.haslocalaccount == 'False') {
authenticationService.logOut();
authenticationService.externalAuthData = {
provider: fragment.provider,
userName: fragment.external_user_name,
externalAccessToken: fragment.external_access_token
};
$location.path('/associate');
}
else {
//Obtain access token and redirect to orders
var externalData = { provider: fragment.provider, externalAccessToken: fragment.external_access_token };
authenticationService.obtainAccessToken(externalData).then(function (response) {
$location.path('/home');
},
function (err) {
$scope.message = err.error_description;
});
}
});
}
init();
function parseQueryString(queryString) {
var data = {},
pairs, pair, separatorIndex, escapedKey, escapedValue, key, value;
if (queryString === null) {
return data;
}
pairs = queryString.split("&");
for (var i = 0; i < pairs.length; i++) {
pair = pairs[i];
separatorIndex = pair.indexOf("=");
if (separatorIndex === -1) {
escapedKey = pair;
escapedValue = null;
} else {
escapedKey = pair.substr(0, separatorIndex);
escapedValue = pair.substr(separatorIndex + 1);
}
key = decodeURIComponent(escapedKey);
value = decodeURIComponent(escapedValue);
data[key] = value;
}
return data;
}
function init() {
var idx = window.location.hash.indexOf("ext=");
if (window.location.hash.indexOf("#") === 0) {
fragment = parseQueryString(window.location.hash.substr(idx));
vm.testFn(fragment);
}
}
Ma ovviamente questo mi sta dando un errore relativo alle angolare (che non ho idea al momento):
https://docs.angularjs.org/error/$rootScope/inprog?p0=$digest
Quindi, praticamente si tratta di un vicolo cieco per me in questa fase.
Qualsiasi idea o contributo sarebbe molto apprezzata.
Gracias!
Aggiornamento: sono riuscito a risolvere l'errore angolare relativo al rootbope lanciato, ma purtroppo la risoluzione non risolve il problema principale. Se provo ad aprire l'accesso social nella stessa scheda del browser in cui si trova la mia applicazione, Google può accedere e tornare all'applicazione e passare i token richiesti. È una storia diversa per Facebook, dove nella console degli strumenti dello sviluppatore c'è un avviso che sembra impedire a Facebook di visualizzare la pagina di accesso.
Praticamente, il metodo originale con cui una nuova finestra (o scheda) viene aperta è la via da seguire, ma il fissaggio degli stessi per il browser mobile sembra essere sempre più difficile.
vorrei prendere tempo per testare questo fuori più tardi il giorno . – Batuta
Non è necessario, 'localStorage' fa parte dell'API HTML5 nel browser, in realtà è nella' finestra', è possibile accedervi da qualsiasi posizione usando 'window.localStorage' – udidu
Ho appena provato questo e ora errore get rootcope Errore: $ rootScope: inprog Azione già in corso – Batuta