Feature: Google and Facebook sign-in
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
using Google.Apis.Oauth2.v2.Data;
|
using Hutopy.Domain.Models;
|
||||||
using Hutopy.Domain.Models;
|
|
||||||
|
|
||||||
namespace Hutopy.Domain.Interfaces;
|
namespace Hutopy.Domain.Interfaces;
|
||||||
|
|
||||||
@@ -7,8 +6,6 @@ public interface IUserService
|
|||||||
{
|
{
|
||||||
Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password);
|
Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password);
|
||||||
|
|
||||||
Task CreateUserAsync(Userinfo userInfo);
|
|
||||||
|
|
||||||
Task<UserModel?> FindUserByIdAsync(string id);
|
Task<UserModel?> FindUserByIdAsync(string id);
|
||||||
Task<UserModel?> GetCurrentUserAsync();
|
Task<UserModel?> GetCurrentUserAsync();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Google.Apis.Oauth2.v2.Data;
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Hutopy.Domain.Interfaces;
|
using Hutopy.Domain.Interfaces;
|
||||||
using Hutopy.Domain.Models;
|
using Hutopy.Domain.Models;
|
||||||
@@ -30,11 +29,6 @@ public class UserService(UserManager<ApplicationUser> userManager, IHttpContextA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateUserAsync(Userinfo userInfo)
|
|
||||||
{
|
|
||||||
await CreateUserAsync(userInfo.Email, userInfo.GivenName, userInfo.GivenName, userInfo.FamilyName, RandomGenerator.RandomString(24));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UserModel?> FindUserByIdAsync(string id)
|
public async Task<UserModel?> FindUserByIdAsync(string id)
|
||||||
{
|
{
|
||||||
var response = await userManager.FindByIdAsync(id);
|
var response = await userManager.FindByIdAsync(id);
|
||||||
|
|||||||
53
src/Web/Controllers/FacebookController.cs
Normal file
53
src/Web/Controllers/FacebookController.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using Hutopy.Domain.Interfaces;
|
||||||
|
using Hutopy.Infrastructure.Services;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.AspNetCore.Authentication.Facebook;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Controllers;
|
||||||
|
|
||||||
|
public class FacebookController(IUserService userService) : Controller
|
||||||
|
{
|
||||||
|
[HttpGet("/api/facebook/sign-in")]
|
||||||
|
public async Task SignIn()
|
||||||
|
{
|
||||||
|
await HttpContext.ChallengeAsync(FacebookDefaults.AuthenticationScheme, new AuthenticationProperties
|
||||||
|
{
|
||||||
|
RedirectUri = Url.Action("Authorize")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Authorize()
|
||||||
|
{
|
||||||
|
var authenticateResult = await HttpContext.AuthenticateAsync(FacebookDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
|
if (!authenticateResult.Succeeded) return BadRequest();
|
||||||
|
|
||||||
|
var claims = authenticateResult.Principal.Claims.ToList();
|
||||||
|
|
||||||
|
var name = claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
|
||||||
|
var email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
|
||||||
|
var givenName = claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value;
|
||||||
|
var familyName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;
|
||||||
|
|
||||||
|
var claimsIdentity = new ClaimsIdentity(new List<Claim>
|
||||||
|
{
|
||||||
|
new(ClaimTypes.Name, name),
|
||||||
|
new(ClaimTypes.Email, email),
|
||||||
|
new(ClaimTypes.GivenName, givenName),
|
||||||
|
new(ClaimTypes.Surname, familyName)
|
||||||
|
}, CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
|
if (await userService.FindUserByEmailAsync(email) != null)
|
||||||
|
{
|
||||||
|
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
|
||||||
|
return Redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
await userService.CreateUserAsync(email, givenName, givenName, familyName, RandomGenerator.RandomString(24));
|
||||||
|
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
|
||||||
|
return Redirect("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Google.Apis.Oauth2.v2.Data;
|
|
||||||
using Hutopy.Domain.Interfaces;
|
using Hutopy.Domain.Interfaces;
|
||||||
|
using Hutopy.Infrastructure.Services;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Authentication.Google;
|
using Microsoft.AspNetCore.Authentication.Google;
|
||||||
@@ -8,52 +8,45 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
|
|
||||||
namespace Hutopy.Web.Controllers;
|
namespace Hutopy.Web.Controllers;
|
||||||
|
|
||||||
public class GoogleController(
|
public class GoogleController(IUserService userService) : Controller
|
||||||
IUserService userService) : Controller
|
|
||||||
{
|
{
|
||||||
[HttpGet("/api/google/sign-in")]
|
[HttpGet("/api/google/sign-in")]
|
||||||
public async Task SignIn()
|
public async Task SignIn()
|
||||||
{
|
{
|
||||||
await HttpContext.ChallengeAsync(GoogleDefaults.AuthenticationScheme, new AuthenticationProperties
|
await HttpContext.ChallengeAsync(GoogleDefaults.AuthenticationScheme, new AuthenticationProperties
|
||||||
{
|
{
|
||||||
RedirectUri = Url.Action("Callback"),
|
RedirectUri = Url.Action("Authorize")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> Callback()
|
public async Task<IActionResult> Authorize()
|
||||||
{
|
{
|
||||||
var authenticateResult = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
var authenticateResult = await HttpContext.AuthenticateAsync(GoogleDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
if (!authenticateResult.Succeeded)
|
if (!authenticateResult.Succeeded) return BadRequest();
|
||||||
{
|
|
||||||
return BadRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
var claims = authenticateResult.Principal.Claims.ToList();
|
var claims = authenticateResult.Principal.Claims.ToList();
|
||||||
|
|
||||||
var userInfo = new Userinfo
|
var name = claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
|
||||||
{
|
var email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
|
||||||
Name = claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value,
|
var givenName = claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value;
|
||||||
Email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value,
|
var familyName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;
|
||||||
GivenName = claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value,
|
|
||||||
FamilyName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
var claimsIdentity = new ClaimsIdentity(new List<Claim>
|
var claimsIdentity = new ClaimsIdentity(new List<Claim>
|
||||||
{
|
{
|
||||||
new(ClaimTypes.Name, userInfo.Name),
|
new(ClaimTypes.Name, name),
|
||||||
new(ClaimTypes.Email, userInfo.Email),
|
new(ClaimTypes.Email, email),
|
||||||
new(ClaimTypes.GivenName, userInfo.GivenName),
|
new(ClaimTypes.GivenName, givenName),
|
||||||
new(ClaimTypes.Surname, userInfo.FamilyName)
|
new(ClaimTypes.Surname, familyName)
|
||||||
}, CookieAuthenticationDefaults.AuthenticationScheme);
|
}, CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
if (await userService.FindUserByEmailAsync(userInfo.Email) != null) // TODO: Do we need to check for null ?
|
if (await userService.FindUserByEmailAsync(email) != null)
|
||||||
{
|
{
|
||||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
|
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
await userService.CreateUserAsync(userInfo);
|
await userService.CreateUserAsync(email, givenName, givenName, familyName, RandomGenerator.RandomString(24));
|
||||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
|
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Hutopy.Web;
|
|||||||
using Azure.Identity;
|
using Azure.Identity;
|
||||||
using Hutopy.Infrastructure.Identity;
|
using Hutopy.Infrastructure.Identity;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.AspNetCore.Authentication.Facebook;
|
||||||
using Microsoft.AspNetCore.Authentication.Google;
|
using Microsoft.AspNetCore.Authentication.Google;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@@ -56,7 +57,7 @@ builder.Services.AddWebServices();
|
|||||||
builder.Services.AddAuthentication(options =>
|
builder.Services.AddAuthentication(options =>
|
||||||
{
|
{
|
||||||
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
|
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
})
|
})
|
||||||
.AddCookie()
|
.AddCookie()
|
||||||
.AddGoogle(
|
.AddGoogle(
|
||||||
@@ -67,6 +68,17 @@ builder.Services.AddAuthentication(options =>
|
|||||||
throw new ArgumentNullException("The Google ClientId is missing.");
|
throw new ArgumentNullException("The Google ClientId is missing.");
|
||||||
options.ClientSecret = builder.Configuration["Google:ClientSecret"] ??
|
options.ClientSecret = builder.Configuration["Google:ClientSecret"] ??
|
||||||
throw new ArgumentNullException("The Google ClientSecret is missing.");
|
throw new ArgumentNullException("The Google ClientSecret is missing.");
|
||||||
|
//options.AccessDeniedPath = "/AccessDeniedPathInfo";
|
||||||
|
})
|
||||||
|
.AddFacebook(
|
||||||
|
FacebookDefaults.AuthenticationScheme,
|
||||||
|
options =>
|
||||||
|
{
|
||||||
|
options.ClientId = builder.Configuration["Facebook:ClientId"] ??
|
||||||
|
throw new ArgumentNullException("The Facebook ClientId is missing.");
|
||||||
|
options.ClientSecret = builder.Configuration["Facebook:ClientSecret"] ??
|
||||||
|
throw new ArgumentNullException("The Facebook ClientSecret is missing.");
|
||||||
|
//options.AccessDeniedPath = "/AccessDeniedPathInfo";
|
||||||
});
|
});
|
||||||
|
|
||||||
// Password hashing
|
// Password hashing
|
||||||
|
|||||||
@@ -685,6 +685,19 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/facebook/sign-in": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Facebook"
|
||||||
|
],
|
||||||
|
"operationId": "Facebook_SignIn",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/google/sign-in": {
|
"/api/google/sign-in": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|||||||
Reference in New Issue
Block a user