Inloggning med Facebook i ASP.NET Core

I det här inlägget beskrivs hur du kan lägga till Facebook-inloggning på din webbplats i ASP.NET Core. Du måste skapa en Facebook Applikation och lägga till Facebook Login som en produkt. Du behöver App ID och App Secret avseende din Facebook App för att kunna använda api:et, du måste också se till att du lägger till din callback url till Valid OAuth Redirect URI i inställningarna för Facebook Login.

När användaren loggar in med sitt Facebook-konto för första gången eller när användaren ansluter sitt konto till Facebook måste du spara användarens Facebook-ID i din databas. Ett Facebook-ID är ett långt heltal (Int64) men kan med fördel sparas som en sträng. Facebook-ID kommer att användas för att hitta användaren när han loggar in för andra gången.

Tjänster

Vi kommer att behöva en HttpClient och en sessionstjänst för att kunna implementera Facebook-inloggning, vi lägger till dessa tjänster i metoden ConfigureServices i klassen StartUp i vårt projekt. Det är inte bra att löpande skapa och kassera HttpClients i ett program, man bör använda statiska HttpClients eller IHttpClientFactory. Vi använder IHttpClientFactory för att skapa en namngiven klient.

// Add memory cache
services.AddDistributedMemoryCache();

// Add a session service
services.AddSession(options =>
{
    // Set session options
    options.IdleTimeout = TimeSpan.FromMinutes(30d);
    options.Cookie.Name = ".Mysite";
    options.Cookie.Path = "/";
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});

// Add clients
services.AddHttpClient("default", client =>
{
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

}).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate });

Vi behöver också använda sessioner i metoden Configure i klassen StartUp.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // Use sessions
    app.UseSession();

    // More code ...

} // End of the Configure method

Modeller

Svar från anrop till Facebook Api:et kommer som JSON och vi har skapat modeller för att kunna arbeta med svarsdatan på ett bekvämt sätt. Dessa modeller har skapats baserat på rena JSON-svar från Facebook Api:et. Det är enkelt att serialisera en modell till JSON och det är enkelt att deserialisera JSON till en modell i C#.

public class FacebookAuthorization
{
    #region Variables

    public string access_token { get; set; }
    public string token_type { get; set; }

    #endregion

    #region Constructors

    public FacebookAuthorization()
    {
        // Set values for instance variables
        this.access_token = null;
        this.token_type = null;

    } // End of the constructor

    #endregion

} // End of the class

public class FacebookErrorRoot
{
    #region Variables

    public FacebookError error { get; set; }

    #endregion

    #region Constructors

    public FacebookErrorRoot()
    {
        // Set values for instance variables
        this.error = null;

    } // End of the constructor

    #endregion

} // End of the class

public class FacebookError
{
    #region Variables

    public string message { get; set; }
    public string type { get; set; }
    public Int32? code { get; set; }
    public Int32? error_subcode { get; set; }
    public string fbtrace_id { get; set; }

    #endregion

    #region Constructors

    public FacebookError()
    {
        this.message = null;
        this.type = null;
        this.code = null;
        this.error_subcode = null;
        this.fbtrace_id = null;

    } // End of the constructor

    #endregion

} // End of the class

public class FacebookUser
{
    #region Variables

    public string id { get; set; }
    public string name { get; set; }

    #endregion

    #region Constructors

    public FacebookUser()
    {
        // Set values for instance variables
        this.id = null;
        this.name = null;

    } // End of the constructor

    #endregion

} // End of the class

Klass för användare

Vi har skapat en klass som innehåller metoder avseende användare och vi har skapat följande metod för att hämta en Facebook-användare. Denna klass injicerar IHttpClientFactory som client_factory och parametern current_domain innehåller Facebook App Id och Facebook App Secret.

/// <summary>
/// Get a facebook user
/// </summary>
public async Task<ModelItem<FacebookUser>> GetFacebookUser(WebDomain current_domain, string code)
{
    // Create variables
    FacebookAuthorization facebook_authorization = null;
    ModelItem<FacebookUser> facebook_user = new ModelItem<FacebookUser>();

    // Get a http client
    HttpClient client = this.client_factory.CreateClient("default");

    // Create the url
    string url = "https://graph.facebook.com/oauth/access_token?client_id=" + current_domain.facebook_app_id + 
    "&redirect_uri=" + current_domain.web_address + "/auth/facebook_login_callback" + "&client_secret=" 
    + current_domain.facebook_app_secret + "&code=" + code;

    // Get the response
    HttpResponseMessage response = await client.GetAsync(url);

    // Make sure that the response is successful
    if (response.IsSuccessStatusCode)
    {
        // Get facebook authorization
        facebook_authorization = JsonConvert.DeserializeObject<FacebookAuthorization>(await 
        response.Content.ReadAsStringAsync());
    }
    else
    {
        // Get an error
        FacebookErrorRoot root = JsonConvert.DeserializeObject<FacebookErrorRoot>(await 
        response.Content.ReadAsStringAsync());
    }

    // Make sure that facebook authorization not is null
    if(facebook_authorization == null)
    {
        return null;
    }

    // Modify the url
    url = "https://graph.facebook.com/me?fields=id,name&access_token=" + facebook_authorization.access_token;

    // Get the response
    response = await client.GetAsync(url);

    // Make sure that the response is successful
    if (response.IsSuccessStatusCode)
    {
        // Get a facebook user
        facebook_user.item = JsonConvert.DeserializeObject<FacebookUser>(await 
        response.Content.ReadAsStringAsync());
    }
    else
    {
        // Get an error
        FacebookErrorRoot root = JsonConvert.DeserializeObject<FacebookErrorRoot>(await 
        response.Content.ReadAsStringAsync());
    }

    // Return the facebook user
    return facebook_user;

} // End of the GetFacebookUser method

Controller

Vår behörighets-controller (authController) innehåller 2 metoder för hantering av Facebook-inloggning. Vi har injicerat IDataProtectionProvider provider och vår användarklass IUserRepository user_repository i denna controller. En IDataProtector data_protector skapas från provider.

// Redirect the user to the facebook login
// GET: /auth/facebook_login?return_url=
[HttpGet]
public async Task<IActionResult> facebook_login(string return_url)
{
    // Get the current domain
    WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

    // Create a random state
    string state = Tools.GeneratePassword();
    ControllerContext.HttpContext.Session.Set<string>("FacebookState", state);
    ControllerContext.HttpContext.Session.Set<string>("FacebookReturnUrl", return_url);

    // Create the url
    string url = "https://www.facebook.com/dialog/oauth?client_id=" + current_domain.facebook_app_id + "&state=" + 
    state + "&response_type=code&redirect_uri=" + current_domain.web_address + "/auth/facebook_login_callback";

    // Redirect the user
    return Redirect(url);

} // End of the facebook_login method

// Login the user with facebook
// GET: /auth/facebook_login_callback
[HttpGet]
public async Task<IActionResult> facebook_login_callback()
{
    // Get the current domain
    WebDomain current_domain = await this.web_domain_repository.GetCurrentDomain(ControllerContext.HttpContext);

    // Get the state
    string state = "";
    if (ControllerContext.HttpContext.Request.Query.ContainsKey("state") == true)
    {
        state = ControllerContext.HttpContext.Request.Query["state"].ToString();
    }

    // Get sessions
    string session_state = ControllerContext.HttpContext.Session.Get<string>("FacebookState");
    string return_url = ControllerContext.HttpContext.Session.Get<string>("FacebookReturnUrl");

    // Get the code
    string code = "";
    if (ControllerContext.HttpContext.Request.Query.ContainsKey("code") == true)
    {
        code = ControllerContext.HttpContext.Request.Query["code"];
    }

    // Make sure that the callback is valid
    if (state != session_state || code == "")
    {
        return Redirect("/");
    }

    // Get a facebook user
    ModelItem<FacebookUser> facebook_user_model = await this.user_repository.GetFacebookUser(current_domain, code);
    FacebookUser facebook_user = facebook_user_model.item;

    // Get the signed in user
    Claim claim = ControllerContext.HttpContext.User.FindFirst("user");
    UserDocument user = claim != null ? JsonConvert.DeserializeObject<UserDocument>(claim.Value) : null;

    // Check if the user exists or not
    if (facebook_user != null && user != null)
    {
        // Update the user
        user.facebook_user_id = facebook_user.id;
        await this.user_repository.Update(user);

        // Redirect the user to the return url
        return Redirect(return_url);
    }
    else if (facebook_user != null && user == null)
    {
        // Check if we can find a user with the facebook id
        ModelItem<UserDocument> user_model = await this.user_repository.GetByFacebookUserId(facebook_user.id);
        user = user_model.item;

        // Check if the user exists
        if (user == null)
        {
            // Create a new user
            user = new UserDocument();
            user.facebook_user_id = facebook_user.id;
            user.public_name = facebook_user.name;
            user.user_email = user.id + "@myfavorite.se";
            user.user_password = PasswordHash.CreateHash(Tools.GeneratePassword());
            user.user_role = "User";

            // Add a user
            await this.user_repository.Upsert(user);

            // Create a cookie
            CookieOptions options = new CookieOptions();
            options.Expires = DateTime.UtcNow.AddDays(30);
            options.HttpOnly = true;
            options.SameSite = SameSiteMode.Lax;
            ControllerContext.HttpContext.Response.Cookies.Append("User", this.data_protector.Protect(user.id), 
            options);

            // Redirect the user to the edit user page
            return Redirect(return_url);
        }
        else
        {
            // Create a cookie
            CookieOptions options = new CookieOptions();
            options.Expires = DateTime.UtcNow.AddDays(30);
            options.HttpOnly = true;
            options.SameSite = SameSiteMode.Lax;
            ControllerContext.HttpContext.Response.Cookies.Append("User", this.data_protector.Protect(user.id), 
            options);

            // Redirect the user to the start page
            return Redirect(return_url);
        }
    }
    else
    {
        // Redirect the user to the login
        return Redirect("/auth/login");
    }

} // End of the facebook_login_callback method

Lämna ett svar

E-postadressen publiceras inte. Obligatoriska fält är märkta *