Después de de esta prolongada selección de información pudimos solucionar esta cuestión que tienen ciertos usuarios. Te regalamos la respuesta y deseamos que resulte de gran apoyo.
Solución:
Si todo lo que quiere hacer es iniciar sesión con Google, no es necesario SignInManager
, UserManager
o la propia identidad de ASP.NET Core. Para lograr esto, primero necesitamos configurar los servicios de Autenticación. Aquí está el código relevante para esto, que explicaré después:
Startup.cs
services
.AddAuthentication(o =>
o.DefaultScheme = "Application";
o.DefaultSignInScheme = "External";
)
.AddCookie("Application")
.AddCookie("External")
.AddGoogle(o =>
o.ClientId = ...;
o.ClientSecret = ...;
);
-
La llamada a
AddAuthentication
configura unDefaultScheme
, que termina siendo utilizado tanto como el Solicitud esquema y el Desafío esquema. los Solicitud El esquema se utiliza al intentar autenticar al usuario (¿ha iniciado sesión?). los Desafío El esquema se utiliza cuando un usuario no ha iniciado sesión pero la aplicación quiere ofrecer la opción de hacerlo. Discutiré elDefaultSignInScheme
más tarde. -
Las dos llamadas a
AddCookie
agregue esquemas de autenticación basados en cookies para ambosApplication
(nuestro Solicitud esquema) yExternal
(nuestro Registrarse esquema).AddCookie
también puede tomar un segundo argumento, que permite la configuración de, por ejemplo, la duración de la cookie correspondiente, etc.
Con esto en su lugar, el proceso de desafío redirigirá al usuario a /Account/Login
(de forma predeterminada, esto también se puede configurar a través de las opciones de autenticación de cookies). Aquí hay una implementación de controlador que maneja el proceso de desafío (nuevamente, lo explicaré después):
AccountController.cs
public class AccountController : Controller
public IActionResult Login(string returnUrl)
return new ChallengeResult(
GoogleDefaults.AuthenticationScheme,
new AuthenticationProperties
RedirectUri = Url.Action(nameof(LoginCallback), new returnUrl )
);
public async Task LoginCallback(string returnUrl)
var authenticateResult = await HttpContext.AuthenticateAsync("External");
if (!authenticateResult.Succeeded)
return BadRequest(); // TODO: Handle this better.
var claimsIdentity = new ClaimsIdentity("Application");
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.Email));
await HttpContext.SignInAsync(
"Application",
new ClaimsPrincipal(claimsIdentity));
return LocalRedirect(returnUrl);
Dividamos esto en dos acciones:
-
Login
Para llegar al
Login
acción, el usuario habrá sido desafiado. Esto ocurre cuando el usuario no ha iniciado sesión con elApplication
esquema pero está intentando acceder a una página protegida por elAuthorize
attribute (o similar). Según su requisito, si el usuario no ha iniciado sesión, queremos iniciar sesión con Google. Para lograrlo, emitimos un nuevo desafío, esta vez para elGoogle
esquema. Lo hacemos usando unChallengeResult
que está configurado con elGoogle
esquema y unRedirectUrl
, que se utiliza para volver a nuestro propio código de aplicación una vez que se completa el proceso de inicio de sesión de Google. Como muestra el código, volvemos a: -
LoginCallback
Aquí es donde el
DefaultSignInScheme
de nuestra llamada aAddAuthentication
se vuelve relevante. Como parte del proceso de inicio de sesión de Google, elDefaultSignInScheme
se utiliza para configurar una cookie que contiene unClaimsPrincipal
representar al usuario como devuelto por Google (todo esto se maneja detrás de escena). La primera línea de código enLoginCallback
agarra estoClaimsPrincipal
instancia, que está envuelto dentro de unAuthenticateResult
que primero se verifica para verificar su éxito. Si todo ha tenido éxito hasta ahora, terminamos creando un nuevoClaimsPrincipal
que contiene los reclamos que necesitamos (tomados de Google en este caso) y luego iniciar sesión en eseClaimsPrincipal
utilizando elApplication
esquema. Por último, redirigimos a la página que provocó nuestra primera desafío.
En respuesta a un par de comentarios / preguntas de seguimiento en los comentarios a continuación:
¿Puedo concluir que el
SignInManager
yUserManager
sólo se utilizan cuando se utiliza la autenticación con una base de datos?
De alguna manera, sí, creo que eso es justo. Aunque es posible implementar un almacén en memoria, en realidad no tiene mucho sentido sin persistencia. Sin embargo, la verdadera razón para no usar estas clases en su situación es simplemente porque no necesita una cuenta de usuario local para representar a un usuario. Eso va de la mano con la perseverancia, pero vale la pena hacer la distinción.
Y qué código muy diferente de lo que leí en el libro (que usé para configurar mi inicio de sesión de Google) y todas las otras respuestas que he leído.
La documentación y los libros cubren el caso de uso más común, en el que hacer desea almacenar usuarios locales que pueden ser vinculado a cuentas externas como Google, etc. Si miras el SignInManager
fuente, verá que en realidad está encima del tipo de código que he mostrado arriba (por ejemplo, aquí y aquí). Se puede encontrar otro código en la interfaz de usuario predeterminada (por ejemplo, aquí) y en AddIdentity
.
Supongo que Google llama a LoginCallback. ¿HttpContext.AuthenticateAsync sabe cómo verificar los datos que me envía Google? Y como su nombre es tan genérico, parece que sabe cómo hacerlo para todos los proveedores externos.
La llamada a AuthenticateAsync
aqui no sabe cualquier cosa acerca de Google: el manejo específico de Google se configura mediante la llamada a AddGoogle
fuera de AddAuthentication
en ConfigureServices
. Después de redirigir a Google para iniciar sesión, volvemos a /signin-google
en nuestra aplicación. Nuevamente, esto se maneja gracias a la llamada a AddGoogle
, pero ese código en realidad solo emite una cookie en el External
esquema que almacena los reclamos que regresaron de Google y luego redirigir a nuestro LoginCallback
endpoint que configuramos. Si agrega una llamada a AddFacebook
, a /sigin-facebook
endpoint se configurará para hacer algo similar. La llamada a AuthenticateAsync
es realmente solo rehidratar un ClaimsPrincipal
a partir de la cookie que fue creada, por ejemplo, por /signin-google
endpoint, para recuperar las reclamaciones.
También vale la pena señalar que el proceso de inicio de sesión de Google / Facebook se basa en el protocolo OAuth 2, por lo que es algo genérico en sí mismo. Si necesitara soporte para algo más que Google, simplemente emitiría el desafío contra el esquema requerido en lugar de codificarlo en Google como lo hice en el ejemplo. También es posible agregar propiedades adicionales al desafío para poder determinar qué proveedor se utilizó cuando su LoginCallback
se alcanza el punto final.
Creé un repositorio de GitHub que contiene un ejemplo completo que construí para escribir esta respuesta aquí.
Valoraciones y comentarios
Si eres capaz, tienes la opción de dejar un enunciado acerca de qué le añadirías a este post.