web-dev-qa-db-fra.com

MVC 5 Owin Facebook Auth entraîne une exception de référence nulle

J'essaie de configurer l'authentification Facebook OWIN intégrée dans un nouveau projet MVC 5 dans Visual Studio 2013. J'ai configuré des applications et des clés selon ce tutoriel:

http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign- activé

Cependant, je reçois une exception NullReferenceException levée à partir de cet appel dans le AccountController:

    [AllowAnonymous]
    public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
    {
        var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();

J'ai déjà vérifié la réponse dans Fiddler et j'obtiens ce qui semble être une réponse positive de Facebook, mais je reçois toujours cette erreur. La réponse ressemble à ceci:

{"id":"xxx","name":"xxx","first_name":"xxx","last_name":"xxx","link":
"https:\/\/www.facebook.com\/profile.php?id=xxx","location":{"id":"xxx","name":"xxx"},
"gender":"xxx","timezone":1,"locale":"en_GB","verified":true,"updated_time":"2013-10-23T10:42:23+0000"}

J'obtiens cela lors du débogage dans http ainsi que https. Je suppose que c'est un bogue de framework mais j'ai jusqu'à présent dessiné un blanc en diagnostiquant cela via le réflecteur.

27
Gracie

Il s'agit probablement d'un bogue dans le code d'extension OWIN d'identité. Je ne peux pas reprocher le problème car ma charge utile facebook renvoie toujours un champ de nom d'utilisateur dans json, qui est absent de votre réponse fb. Je ne sais pas trop pourquoi ce n'est pas là.

Le code dans la méthode d'extension d'identité owin n'a pas de vérification nulle pour la revendication de nom de l'identité qui est identique au champ de nom d'utilisateur. Nous avons déposé un bug pour cela en interne.

Pour contourner ce problème, pourriez-vous essayer de remplacer votre méthode ExternalLoginCallback par le code suivant:

   [AllowAnonymous]
    public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
    {
        var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie);
        if (result == null || result.Identity == null)
        {
            return RedirectToAction("Login");
        }

        var idClaim = result.Identity.FindFirst(ClaimTypes.NameIdentifier);
        if (idClaim == null)
        {
            return RedirectToAction("Login");
        }

        var login = new UserLoginInfo(idClaim.Issuer, idClaim.Value);
        var name = result.Identity.Name == null ? "" : result.Identity.Name.Replace(" ", "");

        // Sign in the user with this external login provider if the user already has a login
        var user = await UserManager.FindAsync(login);
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
            return RedirectToLocal(returnUrl);
        }
        else
        {
            // If the user does not have an account, then Prompt the user to create an account
            ViewBag.ReturnUrl = returnUrl;
            ViewBag.LoginProvider = login.LoginProvider;
            return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = name });
        }
    }

Le code définira le nom d'utilisateur par défaut comme vide lorsqu'il n'y a pas de nom d'utilisateur de retour de Facebook/Google.

26
Hongye Sun

Hongye Sun a fait tout le gros du travail dans sa réponse ci-dessus.

Voici du code qui peut être ajouté à votre classe de contrôleur et être appelé à la place du problème AuthenticationManager.GetExternalLoginInfoAsync ().

private async Task<ExternalLoginInfo> AuthenticationManager_GetExternalLoginInfoAsync_Workaround()
{
    ExternalLoginInfo loginInfo = null;

    var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie);

    if (result != null && result.Identity != null)
    {
        var idClaim = result.Identity.FindFirst(ClaimTypes.NameIdentifier);
        if (idClaim != null)
        {
            loginInfo = new ExternalLoginInfo()
            {
                DefaultUserName = result.Identity.Name == null ? "" : result.Identity.Name.Replace(" ", ""),
                Login = new UserLoginInfo(idClaim.Issuer, idClaim.Value)
            };
        }
    }
    return loginInfo;
}
24
Bryan Knox

J'ai eu le même problème. Je résous mon problème vient d'ajouter app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create); au Startup.Auth.cs. Je n'avais pas ça dans mon Startup.Auth.cs donc

var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);

m'a toujours renvoyé une référence d'objet non définie sur une instance d'une erreur d'objet. Je le comprends en analysant le modèle par défaut VS 2013 pour MVC 5. Donc, si vous avez besoin de plus d'informations sur la structure de code ou un exemple, jetez un œil sur le modèle VS 2013 MVC5.

4
Daniil T.

J'ai rencontré le même problème, lorsque j'ai vérifié les bibliothèques, j'utilisais Microsoft ASP.NET Identity Owin 1.0.0 Je l'ai mis à jour vers Microsoft ASP.NET Identity Owin 2.0.1 à l'aide de la commande PM> Install-Package Microsoft.AspNet.Identity.Owin -Version 2.0.1 Cela a résolu le problème.

2
Rahul Patel

J'ai rencontré ce message il y a quelques jours, mais malheureusement aucune des solutions ci-dessus n'a fonctionné pour moi. voici donc comment j'ai réussi à le réparer et à recevoir l'e-mail de Facebook.

  • Mise à jour suivant NuGet Pacakges
    • Microsoft.Owin Vers la version 3.1.0-rc1
    • Microsoft.Owin.Security Vers la version 3.1.0-rc1
    • Microsoft.Owin.Security.Cookies Vers la version 3.1.0-rc1
    • Microsoft.Owin.Security.OAuth Vers la version 3.1.0-rc1
    • Microsoft.Owin.Security.Facebook Vers la version 3.1.0-rc1

Ajoutez ensuite le code suivant à la classe Identity Startup

var facebookOptions = new FacebookAuthenticationOptions()
        {
            AppId = "your app id",
            AppSecret = "your app secret",
            BackchannelHttpHandler = new FacebookBackChannelHandler(),
            UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name",
            Scope = { "email" }
        };

        app.UseFacebookAuthentication(facebookOptions);

Il s'agit de la classe de définition pour FacebookBackChannelHandler():

using System;
using System.Net.Http;

public class FacebookBackChannelHandler : HttpClientHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        System.Threading.CancellationToken cancellationToken)
    {
        // Replace the RequestUri so it's not malformed
        if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
        {
            request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("?access_token", "&access_token"));
        }

        return await base.SendAsync(request, cancellationToken);
    }
}
2
user3012760

Si votre trace de pile contient DotNetOpenAuth.AspNet, il s'agit du même bogue que celui qui existait apparemment depuis deux ans dans DotNetOpenAuth/DotNetOpenId.

NullReferenceException dans DotNetOpenAuth

https://github.com/DotNetOpenAuth/DotNetOpenAuth/issues/317#issuecomment-29580565

Le propriétaire de ces bibliothèques indique que MS les a abandonnées, bien qu'il semble, d'après votre défaut, qu'elles soient probablement déplacées dans le code MS.

Si oui, cela signifie-t-il que l'OSS a été enterré dans un code fermé?

J'adorerais voir la trace de votre pile.

2
shannon

J'ai commencé à l'obtenir dans le dernier modèle VS 2013.3 et j'ai réalisé que l'authentification ne jouait pas à Nice avec FormsAuthentication que j'avais inutilement porté depuis l'un de mes autres projets. Voici ce que j'ai fait pour le réparer:

ajoutée <system.web><authentication mode="None" />...

ajoutée <system.webServer><modules><remove name="FormsAuthentication" /></modules>...

1
parliament

Je n'avais pas activé l'API Google+ et je suis revenu avec access_denied quand j'ai regardé le violoneux. L'activation de l'API Google+ a résolu le problème.

0
Liam

J'ai eu exactement le même problème en suivant le même tutoriel. Je l'ai résolu en effectuant les deux étapes suivantes: 1> Menu Visual Studio-> Outils-> Gestionnaire de package de bibliothèque-> Gérer les packages NuGet pour la solution ..., puis installer le package: Microsoft.Owin.Host.SystemWeb 2> Dans le même , cliquez sur Mettre à jour (barre de gauche), puis mettez à jour tous les packages.

J'espère que cette réponse aidera d'autres personnes qui ont le même problème.

0
Leona

J'obtenais la même chose.

J'ai remarqué que mes fournisseurs étaient configurés avantUseExternalSignInCookie a été appelé, j'ai donc simplement vérifié que UseExternalSignInCookie est appelé avant que mes fournisseurs soient configurés et que tout fonctionne:

// This has to go first
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

// This must come later
app.UseGoogleAuthentication(
            "[ClientId]",
            "[ClientSecret]");
0
joshcomley

Je pensais que j'allais ajouter quelques notes pour les modèles Visual Studio 2015/le dernier code de plaque de chaudière pour WebAPI 2. Je rencontrais ce problème avec l'authentification Google, mais je pense que c'est similaire à Facebook et aux autres connexions sociales. J'avais le dernier Owin et mes autres paquets de pépites étaient à jour. Il s'avère que avec les derniers modèles d'API Web prêts à l'emploi 2, j'avais juste besoin de demander spécifiquement que "l'e-mail" soit inclus de retour de Google. Sans cette ligne, l'appel api/compte/registre serait une erreur.

Et bien sûr, assurez-vous que votre application est enregistrée auprès de Google et que votre site est autorisé à l'appeler. (Beaucoup de bons exemples montrant ces étapes.) https://console.developers.google.com/apis

Voici mon ajustement dans le fichier App_Start\Startup.Auth.cs:

var googleOptions = new GoogleOAuth2AuthenticationOptions()
{
    ClientId = "xxx",
    ClientSecret = "xxx"
};
googleOptions.Scope.Add("email"); //!! Add this !!
app.UseGoogleAuthentication(googleOptions);

Jusqu'à ce que j'ajoute la ligne .Add ("e-mail"), l'appel api/Account/RegisterExternal WebAPI 2 (AccountController.cs) retournerait null de cette section de RegisterExternal:

var info = await Authentication.GetExternalLoginInfoAsync();
if (info == null) //This would be true, and it would error.
{
      return InternalServerError();
}

Comme il s'agit de l'un des rares articles à proposer cette erreur, je me suis dit que je baliserais mes notes sur ma solution pour la postérité. (en particulier le processus de test du facteur!)

Donc, pour que tout fonctionne dans les tests: 1) Appelez l'URL api/Account/ExternalLogins comme ceci:

http://localhost:59137/api/Account/ExternalLogins?returnUrl=%2F&generateState=true

Vous devriez obtenir une réponse comme celle-ci:

<ArrayOfExternalLoginViewModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/TCG_DL_API.Models">
<ExternalLoginViewModel>
<Name>Google</Name>
<State>1phegLF241xeSfd8gZAsCXiBAp3l5bMygg2VSeRXAHk1</State>
<Url>
/api/Account/ExternalLogin?provider=Google&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A59137%2F&state=1phegLF241xeSfd8gZAsCXiBAp3l5bMygg2VSeRXAHk1
</Url>
</ExternalLoginViewModel>
</ArrayOfExternalLoginViewModel>

2) Obtenez ensuite l'URL de la réponse et appelez-la. Vous devriez obtenir l'invite/page de connexion Google. (Ou je suppose que Facebook ou Twitter, si c'est ce que vous avez configuré.)

3) Connectez-vous et vous serez redirigé vers votre page de redirection. Il aura une URL comme celle-ci:

http: // localhost: 59137/# access_token = d5asC1arCUXaLEMgBS8PT_uwZcTJqC1UZbXblNZ3hMOh3TSKtEXYeKtyKBTv3WmLcaLGGomSvpRSFMfXPxpPvNRgjUVWAiqxtKfv3qWHNqfIMeu5j0eZrJDRAMTrYFgflSbEopAe909a31I4mQnJuvaiITHYPrLmqkm6J88HAVx8F981_q_tflu4A72k3KaB-m2wd0-p1jdQnNMlixM2Wfloh_niUTBIOYUPc1SkKWcZxuI6dzN2Z0PmWHDwzJI8nM8vOuzybJIsxLOyTY1VfzSQ5Qzcll3HhifLPkyZxvXDQ5LHqW1v0_AztsUWkEhW_AJzmw2IaOcTtHCmkmWm1K444okNtOsYfs6HFui0NeY & token_type = porteur & expires_in = 1209600 & state = 3FSOd3_n_sEL4QtiELWPG5B2_H3wRjVb75uDjQS16gk1

prenez le jeton (en gras ci-dessus) et utilisez-le comme jeton de porteur.

Postman example of GET api/Account/UserInfo

4) Maintenant que vous n'êtes pas inscrit (mais vous avez un jeton au porteur), vous pouvez appeler le POST api/Account/RegisterExternal

enter image description here

5) La réponse sera OK, et si vous regardez dans vos tables AspnetUser, vous verrez que vous avez un nouvel enregistrement AspnetUsers et un nouvel enregistrement AspNetUserLogins pour google en tant que fournisseur.

J'espère que cela aide ceux qui essaient de faire fonctionner ces choses!

0
Greg