1654428300
La autenticación es clave para proteger nuestras aplicaciones de usuarios no registrados. En este tutorial, aprenderá a implementar tanto un inicio de sesión de correo electrónico/contraseña tradicional como un inicio de sesión social de Google en Firebase, con el nuevo SDK modular AngularFire v.7.0 que se puede sacudir en árbol, lo que nos permite aprovechar al máximo el nuevo árbol. SDK de Firebase JS movible (v9) .
También aprenderá cómo proteger las rutas con AngularFire Auth Guard y cómo redirigir a los usuarios a ciertas páginas en función de si están autenticados o no.
Tenga en cuenta que, al momento de escribir este tutorial, la API de Angular Fire v.7.0 todavía está en desarrollo y no tiene la función completa ; y los documentos aún no están actualizados a la última versión. AngularFire proporciona una capa de compatibilidad que le permitirá utilizar la última versión de la biblioteca, al mismo tiempo que es totalmente compatible con la API de AngularFire v6.0. Dicho esto, si tienes tanta curiosidad como yo y disfrutas probando las últimas versiones de todas tus bibliotecas, sigue adelante y disfruta de este tutorial.
Aquí hay una vista previa de la aplicación que construiremos hoy:
Vista previa de la aplicación de autenticación
Configuración del proyecto de Firebase
Para crear un nuevo proyecto de Firebase, debemos iniciar sesión en la consola de Firebase. Una vez que hayamos hecho eso, podemos hacer clic en Agregar proyecto para crear un nuevo proyecto:
Firebase — Creando un nuevo proyecto
Tendremos que elegir un nombre para nuestro proyecto:
Firebase: elegir un nombre
Decida si queremos o no Google Analytics en nuestro proyecto (usted elige, me quedo con no):
Firebase: habilitar Google Analytics
¡Hemos terminado! Ahora tenemos un hermoso proyecto nuevo de Firebase.
Ahora necesitamos agregar Firebase a nuestra aplicación Angular. Para hacerlo, debemos hacer clic en el botón de la aplicación web, en el tablero:
Firebase — Agregar una aplicación web
Luego tenemos que elegir un nombre:
Finalmente, ahora tenemos la configuración que se usará más adelante para agregar Firebase a nuestra aplicación Angular:
Es hora de agregar autenticación y elegir los métodos de inicio de sesión que queremos implementar. En este tutorial, agregaremos un inicio de sesión social de Google y un inicio de sesión de correo electrónico/contraseña tradicional. Dirígete a la página de Autenticación y elige los proveedores:
Firebase: elegir métodos de inicio de sesión
Una vez que hayamos habilitado ambos métodos de inicio de sesión, nuestro panel de Autenticación debería verse así:
Firebase: métodos de inicio de sesión habilitados
¡Eso es todo! Ahora tenemos nuestra aplicación Firebase completamente configurada y lista. Es hora de comenzar con nuestra aplicación Angular.
Configuración de un proyecto angular
Lo primero que vamos a hacer es crear un nuevo proyecto con Angular CLI.
Sugerencia: si no ha instalado Angular CLI, puede hacerlo ejecutando el siguiente comando:
npm i -g @angular/cli
Para crear un nuevo proyecto Angular, podemos ejecutar el siguiente comando:
ng new ngbytes-fireauth
Nota: ¡No olvide responder sí cuando se le pregunte si desea agregar enrutamiento a su nueva aplicación!
Una vez que la CLI ha hecho su magia, podemos abrir el proyecto recién creado con nuestro IDE favorito (sugiero VSCode , que es el que normalmente uso).
Agreguemos Firebase y Angularfire a nuestro proyecto. Para ello, utilizaremos el esquema de AngularFire, que se encargará de configurarlo todo por nosotros. Ejecutemos el siguiente comando:
ng add @angular/fire
Se nos hará una serie de preguntas, como qué funciones de Firebase nos gustaría configurar. Para este tutorial, solo necesitamos agregar autenticación, así que seleccionemos eso:
Luego se nos preguntará sobre la cuenta de Firebase que nos gustaría usar y qué proyecto queremos configurar. Seleccione el proyecto que creamos anteriormente y luego seleccione la aplicación que también creamos anteriormente.
Una vez que hayamos hecho todo esto, verá que el esquema se ha ocupado de toda la configuración de Firebase por nosotros. ¡Impresionante!
También agregaremos material angular. Una vez más, usaremos un esquema:
ng add @angular/material
Tendremos que establecer la strictPropertyInitialization
propiedad en el tsconfig.json
archivo en falso. Estamos haciendo esto porque el modo estricto está habilitado de manera predeterminada en todas las nuevas aplicaciones de Angular a partir de la versión 12, lo que significa que TypeScript se quejará si declaramos propiedades de clase sin configurarlas en el constructor (una práctica común en Angular). tsconfig.json
Así es como debería verse nuestro archivo:
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"strictPropertyInitialization": false,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2017",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}
archivo tsconfig.json
Por último, pero no menos importante, eliminaremos el código repetitivo que Angular genera automáticamente en el archivo app.component.html
. Tenga mucho cuidado y asegúrese de no eliminar las <router-outlet></router-outlet>
etiquetas cuando elimine el código repetitivo, o el enrutador no funcionará.
Después de eliminar todo, app.component.html
solo debe contener las etiquetas de salida del enrutador:
etiquetas de salida de enrutador en app.component.html
Creando el tablero
Es hora de comenzar a construir nuestra aplicación Angular. Comencemos por crear un dashboard
módulo. Este es el módulo que contendrá las páginas que queremos proteger de usuarios no autenticados. Comencemos por crear un features
directorio dentro del src/app
directorio, que contendrá todos nuestros módulos de características:
mkdir features
Dentro de este nuevo directorio, crearemos nuestro dashboard
módulo con Angular CLI:
ng g m dashboard -m app --route dashboard
Sugerencia: estamos usando la --route
opción, que crea un componente en el nuevo módulo y agrega la ruta a ese componente en la matriz declarada en el módulo provisto en la opción.Routes
-m
Verás que la CLI ha creado un dashboard
módulo dentro del features
directorio, con su dashboard-routing.module.ts
componente correspondiente. También modificó el app-routing.module.ts
, y agregó una dashboard
ruta, lazy loading el DashboardModule
.
Agreguemos información a nuestro dashboard
componente, modificando el dashboard.component.html
archivo:
<mat-toolbar></mat-toolbar>
<div class="background">
<h1>Welcome to the NgBytes Dashboard</h1>
<h2>Built with <3 by Dottech</h2>
<a target="_blank" class="btn" href="https://github.com/puntotech/ngbytes-fireauth">
Click here to view the source code
</a>
</div>
La plantilla del componente del tablero
Siéntete libre de agregar lo que quieras a este archivo.
Sugerencia: no olvides importar el MatToolbarModule
al DashboardModule
, si decides usarlo también.
Agreguemos un poco de CSS para que el tablero se vea bonito:
h1 {
color: #5eccd6;
font-size: 50px;
font-weight: bolder;
}
h2 {
color: #698893;
margin-bottom: 50px;
}
a {
color: #5eccd6;
border-bottom: 2px solid transparent;
transition: 0.5s border-bottom;
}
mat-toolbar {
background-color: transparent;
display: flex;
justify-content: right;
}
.background {
align-items: center;
background-image: url("/assets/mountains.png");
box-sizing: border-box;
display: flex;
flex-direction: column;
height: calc(100vh - 64px);
padding-top: 10%;
}
.btn {
background-color: #5eccd6;
border-radius: 20px;
color: #fff;
padding: 10px 30px;
text-decoration: none;
transition: 0.5s background-color;
}
a:hover {
border-bottom: 2px solid #5eccd6;
cursor: pointer;
}
.btn:hover {
background-color: #52b7c0;
}
El CSS del componente del tablero
Et voilà, ¡nuestro dashboard
módulo está listo! Para verlo, podemos navegar a http://localhost:4200/dashboard
:
Vista previa del panel
Es hora de comenzar con nuestra autenticación.
Creación del módulo de autenticación
Dentro del features
directorio, usaremos Angular CLI para crear un auth
módulo:
ng g m auth --routing
Sugerencia: estamos usando la --routing
bandera, que genera tanto un auth-routing.module.ts
como elauth.module.ts
.
Una vez que hemos creado nuestro módulo, debemos agregar su ruta correspondiente al app-routing.module.ts
archivo.
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Agregar rutas al módulo de enrutamiento de aplicaciones
Tenga en cuenta que estamos agregando una ruta comodín, que redirige a la ruta predeterminada. Hacemos esto para que, si intentamos navegar a rutas que no existen, en lugar de que se arroje un error, seremos redirigidos.
Todas nuestras interacciones con Firebase y AngularFire se llevarán a cabo dentro del AuthService
. Para crear nuestro servicio, volveremos a utilizar la CLI de Angular. Dentro de nuestro app
directorio:
mkdir core
cd core
mkdir services
cd services
ng g s auth
Dentro de nuestro authService
, lo primero que debemos hacer es inyectar la Auth
instancia de AngularFire en el constructor. Luego, crearemos un login
método, que recibirá los datos de inicio de sesión (correo electrónico y contraseña), y usaremos el signInWithEmailAndPassword
método AngularFire para iniciar sesión:
import { Auth, signInWithEmailAndPassword } from '@angular/fire/auth';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: any) {
return signInWithEmailAndPassword(this.auth, email, password);
}
}
AuthService con método de inicio de sesión sin tipo
Sin embargo, ¿no parece algo extraño en nuestra login
función? Tenemos un muy indecoroso any
! Como estamos usando TypeScript y queremos tener todo bien escrito, creemos una LoginData
interfaz para escribir nuestros datos de inicio de sesión. En nuestro core
directorio:
mkdir interfaces
cd interfaces
touch login-data.interface.ts
Así es como se verá nuestra interfaz:
export interface LoginData {
email: string;
password: string;
}
La interfaz LoginData https://gist.github.com/NyaGarcia/2ec63c5fe25c1d281fcdb011722e9712
Ahora podemos usar nuestra nueva interfaz para escribir correctamente login
los parámetros de nuestra función:
import { Auth, signInWithEmailAndPassword } from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { LoginData } from '../interfaces/login-data.interface';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: LoginData) {
return signInWithEmailAndPassword(this.auth, email, password);
}
}
AuthService con función de inicio de sesión escrita
Se ve mucho mejor, ¿no?
También crearemos un método de registro, que usará el createUserWithEmailAndPassword
método AngularFire, para que los nuevos usuarios puedan registrarse en nuestra aplicación. También crearemos un logout
método que, como probablemente pueda adivinar, permitirá a nuestros usuarios cerrar sesión en nuestra aplicación:
import {
Auth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
} from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { LoginData } from '../interfaces/login-data.interface';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: LoginData) {
return signInWithEmailAndPassword(this.auth, email, password);
}
register({ email, password }: LoginData) {
return createUserWithEmailAndPassword(this.auth, email, password);
}
logout() {
return signOut(this.auth);
}
}
Agregar las funciones de registro y cierre de sesión al AuthService
Eso es todo lo que tenemos que hacer en nuestro AuthService
(agregaremos la opción de inicio de sesión social de Google más adelante) por ahora.
Ahora es el momento de crear los formularios de registro e inicio de sesión, para que nuestros usuarios puedan autenticarse.
Dentro del auth
directorio, crearemos un login-page
componente, que mostrará un mensaje de bienvenida, el formulario de inicio de sesión para que los usuarios puedan iniciar sesión y, más adelante, el botón de inicio de sesión de Google:
mkdir pages
cd pages
ng g c login-page
Una vez que hayamos creado nuestra página, vayamos al login-page.component.html
archivo y agreguemos un título simple:
<h1>Welcome to NgBytes Login</h1>
Dado que las páginas son componentes enrutados, debemos agregar una ruta a nuestro auth-routing.module.ts
archivo que apunte al LoginPageComponent
, así:
import { RouterModule, Routes } from '@angular/router';
import { LoginPageComponent } from './pages/login-page/login-page.component';
import { NgModule } from '@angular/core';
const routes: Routes = [{ path: '', component: LoginPageComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AuthRoutingModule {}
Enrutamiento de LoginPageComponent en AuthRoutingModule
Ejecutemos el npm start
script y verifiquemos que todo esté enrutado correctamente. Si abre su navegador a localhost:4200
, debería estar viendo esto:
Vista previa de LoginPageComponent
¡Impresionante! Es hora de crear un formulario de inicio de sesión, para que nuestros usuarios puedan iniciar sesión en nuestra aplicación:
Ahora crearemos un login-form
componente, dentro de nuestro auth
directorio:
mkdir components
cd components
ng g c login-form
Sugerencia: dado que no escribiremos ninguna prueba en este tutorial, no dude en eliminar el login-form.component.spec.ts
archivo.
Para crear nuestro formulario, usaremos el módulo Formularios reactivos. También usaremos los módulos de material angular para diseñar nuestro formulario. Dado que no es el objetivo de este tutorial, no entraré en detalles sobre cómo usar formularios reactivos.
Primero, importaremos los módulos ReactiveFormsModule
, the MatInputModule
, the MatFormFieldModule
y the en nuestro :MatButtonModuleAuthModule
import { AuthRoutingModule } from './auth-routing.module';
import { CommonModule } from '@angular/common';
import { LoginFormComponent } from './components/login-form/login-form.component';
import { LoginPageComponent } from './pages/login-page/login-page.component';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [LoginPageComponent, LoginFormComponent],
imports: [
CommonModule,
AuthRoutingModule,
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
MatButtonModule,
],
})
export class AuthModule {}
Importación de módulos en AuthModule
Luego, crearemos el formulario en nuestro login-form.component.ts
archivo:
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'ngbytes-login-form',
templateUrl: './login-form.component.html',
styleUrls: ['./login-form.component.css'],
})
export class LoginFormComponent implements OnInit {
@Output() formData: EventEmitter<{
email: string;
password: string;
}> = new EventEmitter();
form: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
this.form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', Validators.required],
});
}
get email() {
return this.form.get('email');
}
get password() {
return this.form.get('password');
}
onSubmit() {
this.formData.emit(this.form.value);
}
}
La lógica de LoginFormComponent
Como puede ver, hemos agregado una validación básica a nuestro formulario: tanto el campo de correo electrónico como el de contraseña son obligatorios, y el campo de correo electrónico solo permite direcciones de correo electrónico válidas.
Sugerencia: ¿Le parece extraño que estemos usando un EventEmitter
correo electrónico para emitir los datos de nuestro formulario al enviarlo? Esto se debe a que estamos usando el patrón de componente inteligente/tonto, que desvincula la lógica comercial de los componentes puramente de presentación.
Por último, en nuestro login-form.component.html
archivo:
<form class="form" [formGroup]="form" (ngSubmit)="onSubmit()">
<mat-form-field class="form-control">
<mat-label>Email</mat-label>
<input matInput formControlName="email" type="text" required />
<mat-error *ngIf="email?.hasError('required')">
Email is required
</mat-error>
<mat-error *ngIf="email?.hasError('email')">
Email isn't valid. Please enter a valid email.
</mat-error>
</mat-form-field>
<mat-form-field class="form-control">
<mat-label>Password</mat-label>
<input matInput formControlName="password" type="password" required />
<mat-error *ngIf="password?.hasError('required')">
Password is required
</mat-error>
</mat-form-field>
<div class="form-footer">
<button [disabled]="form.invalid" mat-raised-button type="submit">
Submit
</button>
</div>
</form>
La plantilla LoginFormComponent
Nota: ¿Ves cómo estamos deshabilitando el botón de enviar si el formulario no es válido? Esa es una mala práctica (UX deficiente), y la única razón por la que lo hago es por brevedad, ya que este tutorial debe centrarse en AngularFire y no en las mejores prácticas en formularios Angular. Sin embargo, tenga cuidado, ¡no haga esto en casa, amigos!
Agreguemos un poco de CSS en nuestro login-form.component.css
archivo para que el formulario sea más bonito (puede omitir este paso si lo desea):
button {
background-color: #5eccd6;
border-radius: 20px;
color: #fff;
padding: 0 50px;
}
form {
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-evenly;
height: 200px;
width: 300px;
}
mat-form-field {
width: 100%;
}
El CSS del componente LoginForm
¡Excelente! Ahora tenemos un hermoso formulario de inicio de sesión. Todo lo que queda es agregarlo a la login-page.component.html
plantilla:
<div class="form-container">
<h1>Welcome to NgBytes Login</h1>
<h5>Sign in with your email</h5>
<ngbytes-login-form (formData)="login($event)"></ngbytes-login-form>
</div>
Adición del formulario de inicio de sesión a la página de inicio de sesión
También necesitamos inyectar lo authService
que creamos anteriormente en nuestro login-page.component.ts
archivo y crear un login
método, que usará authService
para iniciar sesión en el usuario, redirigiéndolo al tablero una vez que haya terminado. ¡No olvides detectar cualquier posible error!
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/core/services/auth.service';
import { LoginData } from 'src/app/core/interfaces/login-data.interface';
import { Router } from '@angular/router';
@Component({
selector: 'app-login-page',
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.css'],
})
export class LoginPageComponent implements OnInit {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit(): void {}
login(loginData: LoginData) {
this.authService
.login(loginData)
.then(() => this.router.navigate(['/dashboard']))
.catch((e) => console.log(e.message));
}
}
Creación de un método de inicio de sesión en login-page.component
Probemos lo que hemos hecho hasta ahora yendo a nuestro navegador y completando el formulario. Como puede ver, si intentamos dejar el campo de correo electrónico en blanco, o ingresar un correo electrónico no válido, se mostrará un error, lo que significa que nuestra validación está funcionando espléndidamente. Sin embargo, si intentamos presionar el botón de enviar…
Estamos recibiendo un error de Usuario no encontrado en nuestra consola. Bueno, por supuesto que lo somos. Todavía no hemos registrado ningún usuario, ¿verdad? ¡Creemos un formulario de registro para que podamos hacerlo!
Al igual que hicimos con nuestra página de inicio de sesión, vamos a crear una página de registro, que contendrá el formulario de registro. Dentro del auth/pages
directorio:
ng g c register-page
Agreguemos un título a nuestra página de registro y algunos estilos:
<div class="container">
<h1>Welcome to NgBytes Register</h1>
</div>
La plantilla RegisterPageComponent
h1 {
color: #838383;
margin-bottom: 50px;
}
.container {
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
margin-top: 80px;
}
El CSS de RegisterPageComponent https://gist.github.com/NyaGarcia/f6f3f5892f711fa31fa4acac1c886968
Por último, pero no menos importante, debemos agregar una register
ruta al auth-routing.module.ts
archivo:
import { RouterModule, Routes } from '@angular/router';
import { LoginPageComponent } from './pages/login-page/login-page.component';
import { NgModule } from '@angular/core';
import { RegisterPageComponent } from './pages/register-page/register-page.component';
const routes: Routes = [
{ path: '', component: LoginPageComponent },
{ path: 'register', component: RegisterPageComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AuthRoutingModule {}
Agregar la ruta RegisterPageComponent
Ya que nuestra página de registro está lista, todo lo que tenemos que hacer es crear un formulario de registro... ¿o sí?
Nuestro formulario de inicio de sesión tiene dos campos, un correo electrónico y un campo de contraseña. Nuestro formulario de registro debe tener dos campos, un correo electrónico y un campo de contraseña. ¿Ves a dónde estoy llegando? ¡Nuestras dos formas van a ser exactamente iguales! Puede que estés pensando que no, que no son lo mismo. Un formulario se utiliza para iniciar sesión, el otro para registrar un nuevo usuario; dos piezas de lógica completamente diferentes.
Sin embargo…
¿Recuerda cómo usamos el patrón de componente inteligente/tonto para desacoplar el formulario de inicio de sesión de la página de inicio de sesión? Nuestro formulario de inicio de sesión no está a cargo de realizar ninguna lógica en absoluto, nuestra página de inicio de sesión sí lo está. Lo que significa que somos libres de reutilizar el formulario de inicio de sesión en nuestra página de registro:
<div class="container">
<h1>Welcome to NgBytes Register</h1>
<ngbytes-login-form (formData)="register($event)"></ngbytes-login-form>
</div>
Agregar el formulario de inicio de sesión a la página de registro
Genial, ¿no? Ahora llega el momento de implementar la lógica para dar de alta un nuevo usuario. Así lo haremos, por supuesto, en nuestra register-page.component.ts
ficha:
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/core/services/auth.service';
import { LoginData } from 'src/app/core/interfaces/login-data.interface';
import { Router } from '@angular/router';
@Component({
selector: 'app-register-page',
templateUrl: './register-page.component.html',
styleUrls: ['./register-page.component.css'],
})
export class RegisterPageComponent implements OnInit {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit(): void {}
register(data: LoginData) {
this.authService
.register(data)
.then(() => this.router.navigate(['/login']))
.catch((e) => console.log(e.message));
}
}
Agregar la lógica de registro a RegisterPageComponent
Como puede ver, estamos llamando al authService.register
método, que registrará al nuevo usuario en nuestra aplicación Firebase, y luego estamos navegando a la página de inicio de sesión, para que nuestro usuario recién registrado pueda iniciar sesión en nuestra aplicación.
Agreguemos algunos toques finales a la página de inicio de sesión: un botón que redirigirá a los nuevos usuarios a la página de registro y cambiará un poco el diseño para que se vea decente:
<div class="background">
<div class="login-container">
<div class="text-container">
<h1>Hey Stranger</h1>
<p>
Want to create an awesome account to access this awesome app? Go ahead
and click the button below!
</p>
<button mat-button routerLink="/register">Sign up</button>
</div>
<div class="form-container">
<h1>Welcome to NgBytes Login</h1>
<h5>Sign in with your email</h5>
<ngbytes-login-form (formData)="login($event)"></ngbytes-login-form>
</div>
</div>
</div>
Mejorando la página de inicio de sesión
Un poco de magia CSS:
a {
color: #838383;
margin-top: 30px;
text-decoration: none;
}
h1 {
color: #5eccd6;
font-weight: bolder;
margin-bottom: 20px;
}
h5 {
color: #838383;
}
button {
border-radius: 20px;
padding: 0 50px;
}
p {
margin-bottom: 20px;
text-align: center;
}
.background {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
}
.login-container {
border-radius: 10px;
box-shadow: 0 0 20px 2px #e4e4e4;
display: flex;
width: 60%;
}
.text-container {
align-items: center;
background-color: #5eccd6;
border-radius: 10px 0 0 10px;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
padding: 50px;
width: 30%;
}
.form-container {
align-items: center;
background-color: #fff;
border-radius: 0 10px 10px 0;
display: flex;
flex-direction: column;
justify-content: center;
padding: 80px;
width: 70%;
}
.text-container h1 {
color: #fff;
}
.text-container button {
background-color: transparent;
border: 1px solid #fff;
}
Agregar estilos a la página de inicio de sesión
Ya que hemos hecho que nuestra página de inicio de sesión se vea bonita, tomemos un momento y hagamos lo mismo con nuestra página de registro:
<div class="background">
<div class="login-container">
<div class="form-container">
<h1>Welcome to NgBytes Register</h1>
<h5>Register with your email</h5>
<ngbytes-login-form (formData)="register($event)"></ngbytes-login-form>
</div>
<div class="text-container">
<h1>Hey Buddy</h1>
<p>
Already have an awesome account? Go ahead and click the button below to
sign in!
</p>
<button mat-button routerLink="/">Sign in</button>
</div>
</div>
</div>
Mejorando la página de registro
Un poco más de CSS:
a {
color: #838383;
margin-top: 30px;
text-decoration: none;
}
h1 {
color: #5eccd6;
font-weight: bolder;
margin-bottom: 20px;
}
h5 {
color: #838383;
}
button {
border-radius: 20px;
padding: 0 50px;
}
p {
margin-bottom: 20px;
text-align: center;
}
.background {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
}
.login-container {
border-radius: 10px;
box-shadow: 0 0 20px 2px #e4e4e4;
display: flex;
width: 60%;
}
.text-container {
align-items: center;
background-color: #5eccd6;
border-radius: 0 10px 10px 0;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
padding: 50px;
width: 30%;
}
.form-container {
align-items: center;
background-color: #fff;
border-radius: 10px 0 0 10px;
display: flex;
flex-direction: column;
justify-content: center;
padding: 80px;
width: 70%;
}
.text-container h1 {
color: #fff;
}
.text-container button {
background-color: transparent;
border: 1px solid #fff;
}
Agregar estilos a la página de registro
Es hora de probar lo que hemos hecho hasta ahora. Dirígete a tu navegador:
Registro de un nuevo usuario e inicio de sesión
¡Impresionante! ¡Estamos iniciando sesión en nuestra aplicación! Sin embargo, todavía nos falta algo, ¿no? ¡Necesitamos una opción de cierre de sesión! Vamos a agregarlo a nuestro tablero.
Vayamos a nuestro dashboard.component.html
y creemos una opción de cierre de sesión:
<mat-toolbar><a (click)="logout()">Log out</a></mat-toolbar>
<div class="background">
<h1>Welcome to the NgBytes Dashboard</h1>
<h2>Built with <3 by Dottech</h2>
<a
target="_blank"
class="btn"
href="https://github.com/puntotech/ngbytes-fireauth"
>
Click here to view the source code
</a>
</div>
Agregar una opción de cierre de sesión al tablero
Ahora que tenemos nuestra opción de cierre de sesión, necesitamos agregar algo de lógica a nuestro archivo dashboard.component.ts
. Primero, inyectaremos el AuthService
en el constructor y luego crearemos un logout
método:
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../core/services/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
})
export class DashboardComponent implements OnInit {
constructor(private authService: AuthService, private router: Router) {}
ngOnInit(): void {}
logout() {
this.authService
.logout()
.then(() => this.router.navigate(['/']))
.catch((e) => console.log(e.message));
}
}
Agregar un método de cierre de sesión al componente del tablero
Como puede ver, una vez que llamemos al logout
método, redirigiremos al usuario a la ruta predeterminada. ¡Eso es todo! Hemos implementado con éxito una forma para que nuestros usuarios cierren sesión en nuestra aplicación. Una vez más, probemos nuestra aplicación:
Saliendo de tu cuenta
Adición de inicio de sesión de Google
Ahora que nuestro inicio de sesión tradicional se ha implementado por completo, es hora de agregar nuestro inicio de sesión social de Google. Para hacerlo, primero crearemos el método necesario en el auth.service
:
import {
Auth,
GoogleAuthProvider,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signInWithPopup,
signOut,
} from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { LoginData } from '../interfaces/login-data.interface';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: LoginData) {
return signInWithEmailAndPassword(this.auth, email, password);
}
loginWithGoogle() {
return signInWithPopup(this.auth, new GoogleAuthProvider());
}
register({ email, password }: LoginData) {
return createUserWithEmailAndPassword(this.auth, email, password);
}
logout() {
return signOut(this.auth);
}
}
Agregar un método loginWithGoogle al AuthService
Luego, necesitaremos agregar un botón de inicio de sesión de Google a la página de inicio de sesión:
<div class="background">
<div class="login-container">
<div class="text-container">
<h1>Hey Stranger</h1>
<p>
Want to create an awesome account to access this awesome app? Go ahead
and click the button below!
</p>
<button mat-button routerLink="/register">Sign up</button>
</div>
<div class="form-container">
<h1>Welcome to NgBytes Login</h1>
<button class="mat-raised-button" (click)="loginWithGoogle()">
Sign In with Google
</button>
<h5>Or sign in with your email</h5>
<ngbytes-login-form (formData)="login($event)"></ngbytes-login-form>
</div>
</div>
</div>
Agregar un botón de inicio de sesión de Google
Finalmente, necesitaremos agregar una loginWithGoogle
función a nuestro login-page.component.ts
:
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/core/services/auth.service';
import { LoginData } from 'src/app/core/interfaces/login-data.interface';
import { Router } from '@angular/router';
@Component({
selector: 'app-login-page',
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.css'],
})
export class LoginPageComponent implements OnInit {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit(): void {}
login(loginData: LoginData) {
this.authService
.login(loginData)
.then(() => this.router.navigate(['/dashboard']))
.catch((e) => console.log(e.message));
}
loginWithGoogle() {
this.authService
.loginWithGoogle()
.then(() => this.router.navigate(['/dashboard']))
.catch((e) => console.log(e.message));
}
}
Agregar un método loginWithGoogle al LoginPageComponent
¡Hemos terminado! Vayamos a nuestro navegador y pruébelo:
Iniciar sesión con google
Protegiendo las rutas
Si intenta navegar a la página del panel localhost:4200/dashboard
, verá que podrá hacerlo, incluso si no está autenticado. Para resolver este problema, utilizaremos la protección de autenticación proporcionada por AngularFire para redirigir automáticamente a los usuarios no autorizados a la página de inicio de sesión. En nuestro app-routing.module.ts
:
import { AuthGuard, redirectUnauthorizedTo } from '@angular/fire/auth-guard';
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['']);
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectUnauthorizedToLogin },
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Protegiendo las rutas
Como puede ver, hemos protegido la dashboard
ruta con AngularFire AuthGuard
y hemos construido una redirectUnauthorizedToLogin
canalización, utilizando la redirectUnauthorizedTo
canalización AngularFire, que luego proporcionamos a AuthGuard
. Esto garantizará que cualquier usuario no autenticado que intente acceder a la página del panel de control será redirigido automáticamente a la página de inicio de sesión. Siéntase libre de dirigirse a su navegador y probarlo.
De la misma manera que no queremos que los usuarios no autorizados accedan al panel, si el usuario ya está autenticado, no debería poder navegar a la página de inicio de sesión. Vamos a crear otra canalización personalizada, una que redirigirá a los usuarios que ya iniciaron sesión al tablero:
import {
AuthGuard,
redirectLoggedInTo,
redirectUnauthorizedTo,
} from '@angular/fire/auth-guard';
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['']);
const redirectLoggedInToHome = () => redirectLoggedInTo(['dashboard']);
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectLoggedInToHome },
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectUnauthorizedToLogin },
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Agregar el protector de ruta redirectLoggedInToHome
Ahora, si hemos iniciado sesión e intentamos acceder a la página de inicio de sesión, seremos redirigidos automáticamente al panel de control.
AngularFire nos proporciona un canActivate
asistente, que podemos usar junto con el operador de propagación, para hacer que nuestras rutas sean más legibles:
import { RouterModule, Routes } from '@angular/router';
import {
canActivate,
redirectLoggedInTo,
redirectUnauthorizedTo,
} from '@angular/fire/auth-guard';
import { NgModule } from '@angular/core';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['']);
const redirectLoggedInToHome = () => redirectLoggedInTo(['dashboard']);
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
...canActivate(redirectLoggedInToHome),
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
...canActivate(redirectUnauthorizedToLogin),
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Usando el ayudante canActivate para hacer las rutas más legibles
Conclusión
¡Eso es todo amigos! Enhorabuena, ha creado correctamente una aplicación de autenticación con Angular 13 y el último SDK modular de AngularFire 7.
Recuerde que la nueva API no está completa, por lo que si decide seguir adelante de todos modos y usarla en producción, hágalo bajo su propio riesgo. Además, tenga en cuenta que AngularFire 7 proporciona una capa de compatibilidad para que pueda continuar usando la API anterior de AngularFire 6.
Espero que hayas encontrado útil este tutorial. Muchas gracias por leer.
Esta historia se publicó originalmente en https://betterprogramming.pub/angular-13-firebase-authentication-tutorial-with-angularfire-7-23dc8cee42c4
1588747755
In this tutorial, I am going to share with you How to Deploy Angular 10/9 App to Production with Firebase Hosting?.
#angular #firebase #popular tutorials #angualr firebase deploy #angular app #angular tutorial #firebase tutorial
1654403820
Authentication is key to protecting our apps from unregistered users. In this tutorial, you will learn how to implement both a traditional email/password login and a Google social login in Firebase, with the new tree-shakable AngularFire v.7.0 modular SDK, which allows us to take full advantage of the new tree-shakable Firebase JS SDK (v9).
You’ll also learn how to protect routes with the AngularFire Auth Guard, and how to redirect users to certain pages based on whether they’re authenticated or not.
Please note that, at the time of writing this tutorial, the Angular fire v.7.0 API is still in development, and isn’t feature complete; and the docs aren’t yet updated to the latest version. AngularFire provides a compatibility layer that will allow you to use the latest version of the library, while fully supporting the AngularFire v6.0 API. This said, if you’re as curious as I am, and enjoy trying out the latest versions of all your libraries, go ahead and enjoy this tutorial.
Here’s a preview of the app we’ll be building today:
Preview of the authentication app
Setting up Firebase project
To create a new Firebase project, we need to log in to the Firebase console. Once we’ve done that, we can click on Add Project to create a new project:
Firebase — Creating a new project
We’ll need to choose a name for our project:
Firebase — Choosing a name
Decide whether or not we want Google Analytics in our project (your choice, I’ll go with no):
Firebase — Enabling Google Analytics
We’re done! We now have a lovely new Firebase project.
Now we need to add Firebase to our Angular app. To do so, we need to click on the Web app button, in the dashboard:
Firebase — Adding a web app
We then need to choose a name:
Finally, we now have the configuration that will be used later on to add Firebase to our Angular application:
It’s time to add authentication and choose the sign-in methods that we want to implement. In this tutorial, we’ll add a Google social login, and a traditional email/password login. Head over to the Authentication page, and choose the providers:
Firebase — Choosing sign-in methods
Once we’ve enabled both sign-in methods, our Authentication dashboard should look like this:
Firebase — Enabled sign-in methods
That’s all! We now have our Firebase app fully set up and ready. It’s time to get started on our Angular app.
Setting up an Angular project
The first thing we’re going to do is create a new project with the Angular CLI.
Tip: If you haven’t installed the Angular CLI, you can do so by running the following command:
npm i -g @angular/cli
To create a new Angular project, we can run the following command:
ng new ngbytes-fireauth
Note: Don’t forget to answer yes when you’re asked if you want to add routing to your new app!
Once the CLI has worked its magic, we can open the newly created project with our favorite IDE (I suggest VSCode, which is the one I normally use).
Let’s add Firebase and Angularfire to our project. To do so, we’ll use the AngularFire schematic, that will take care of setting everything up for us. Let’s run the following command:
ng add @angular/fire
We’ll be asked a series of questions, like which Firebase features we’d like to setup. For this tutorial, we only need to add authentication, so let’s select that:
We’ll then be asked about the Firebase account we’d like to use, and which project we want to setup. Select the project we created previously, and then select the app we also created earlier.
Once we’ve done all this, you will see that the schematic has taken care of all the Firebase configuration for us. Awesome!
We’ll also add Angular Material. Once again, we’ll use a schematic:
ng add @angular/material
We’ll need to set the strictPropertyInitialization
property in the tsconfig.json
file to false. We’re doing this because the strict mode is enabled by default in all new Angular apps starting from version 12, which means that TypeScript will complain if we declare any class properties without setting them in the constructor (a common practice in Angular). Here’s what our tsconfig.json
file should look like:
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"strictPropertyInitialization": false,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2017",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}
tsconfig.json file
Last, but not least, we’ll delete the boilerplate code that Angular automatically generates in the app.component.html
. Be very careful and make sure that you do not delete the <router-outlet></router-outlet>
tags when you delete the boilerplate code, or the router won’t work.
After deleting everything, your app.component.html
should only contain the router-outlet tags:
router-outlet tags in the app.component.html
Creating the dashboard
It’s time to start building our Angular app. Let’s begin by creating a dashboard
module. This is the module that will contain the pages which we want to protect from unauthenticated users. Let’s begin by creating a features
directory inside the src/app
directory, which will contain all of our feature modules:
mkdir features
Inside this new directory, we’ll create our dashboard
module with the Angular CLI:
ng g m dashboard -m app --route dashboard
Tip: We’re using the --route
option, which creates a component in the new module, and adds the route to that component in the Routes
array declared in the module provided in the -m
option.
You’ll see that the CLI has created a dashboard
module inside the features
directory, with its corresponding dashboard-routing.module.ts
and component. It’s also modified the app-routing.module.ts
, and added a dashboard
route, lazy loading the DashboardModule
.
Let’s add some information to our the dashboard
component, by modifying the dashboard.component.html
file:
<mat-toolbar></mat-toolbar>
<div class="background">
<h1>Welcome to the NgBytes Dashboard</h1>
<h2>Built with <3 by Dottech</h2>
<a target="_blank" class="btn" href="https://github.com/puntotech/ngbytes-fireauth">
Click here to view the source code
</a>
</div>
The dashboard component template
Feel free to add whatever you want to this file.
Tip: Don’t forget to import the MatToolbarModule
to the DashboardModule
, if you decide to use it too.
Let’s add a little CSS, to make the dashboard look pretty:
h1 {
color: #5eccd6;
font-size: 50px;
font-weight: bolder;
}
h2 {
color: #698893;
margin-bottom: 50px;
}
a {
color: #5eccd6;
border-bottom: 2px solid transparent;
transition: 0.5s border-bottom;
}
mat-toolbar {
background-color: transparent;
display: flex;
justify-content: right;
}
.background {
align-items: center;
background-image: url("/assets/mountains.png");
box-sizing: border-box;
display: flex;
flex-direction: column;
height: calc(100vh - 64px);
padding-top: 10%;
}
.btn {
background-color: #5eccd6;
border-radius: 20px;
color: #fff;
padding: 10px 30px;
text-decoration: none;
transition: 0.5s background-color;
}
a:hover {
border-bottom: 2px solid #5eccd6;
cursor: pointer;
}
.btn:hover {
background-color: #52b7c0;
}
The dashboard component CSS
Et voilà, our dashboard
module is ready! To see it, we can navigate to http://localhost:4200/dashboard
:
Dashboard preview
It’s time to get started on our authentication.
Creating the Auth Module
Inside the features
directory, we’ll use the Angular CLI to create an auth
module:
ng g m auth --routing
Tip: We’re using the --routing
flag, which generates an auth-routing.module.ts
as well as the auth.module.ts
.
Once we’ve created our module, we need to add its corresponding route to the app-routing.module.ts
file.
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Adding routes the app-routing module
Note that we’re adding a wildcard route, that redirects to the default route. We do this so that if we try to navigate to any routes that don’t exist, instead of an error being thrown, we’ll be redirected.
All of our interactions with Firebase and AngularFire will take place within the AuthService
. To create our service, we’ll once again make use of the Angular CLI. Inside our app
directory:
mkdir core
cd core
mkdir services
cd services
ng g s auth
Inside our authService
, the first thing we’ll need to do is inject the AngularFire Auth
instance in the constructor. Then, we’ll create a login
method, which will receive the login data (email and password), and use the signInWithEmailAndPassword
AngularFire method to sign in:
import { Auth, signInWithEmailAndPassword } from '@angular/fire/auth';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: any) {
return signInWithEmailAndPassword(this.auth, email, password);
}
}
AuthService with untyped login method
However, doesn’t something about our login
function seem a little off? We have a very unseemly any
! Since we’re using TypeScript, and we want to have everything nicely typed, let’s create a LoginData
interface, to type our login data. In our core
directory:
mkdir interfaces
cd interfaces
touch login-data.interface.ts
Here’s what out interface will look like:
export interface LoginData {
email: string;
password: string;
}
The LoginData interface https://gist.github.com/NyaGarcia/2ec63c5fe25c1d281fcdb011722e9712
Now we can use our new interface to properly type our login
function’s parameters:
import { Auth, signInWithEmailAndPassword } from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { LoginData } from '../interfaces/login-data.interface';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: LoginData) {
return signInWithEmailAndPassword(this.auth, email, password);
}
}
AuthService with typed login function
Looking much better, isn’t it?
We’ll also create a register method, which will use the AngularFire createUserWithEmailAndPassword
method, so that new users can be registered in our app. We’ll also create a logout
method, which, as you can probably guess, will allow our users to log out of our application:
import {
Auth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
} from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { LoginData } from '../interfaces/login-data.interface';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: LoginData) {
return signInWithEmailAndPassword(this.auth, email, password);
}
register({ email, password }: LoginData) {
return createUserWithEmailAndPassword(this.auth, email, password);
}
logout() {
return signOut(this.auth);
}
}
Adding the register and logout functions to the AuthService
That’s all we have to do in our AuthService
(we’ll be adding the Google social login option later on) for now.
It’s now time to create the register and login forms, so that our users can be authenticated.
Inside the auth
directory, we’ll create a login-page
component, which will display a welcome message, the login form so that users can sign in, and later on, the google login button:
mkdir pages
cd pages
ng g c login-page
Once we’ve created our page, let’s head over to the login-page.component.html
file and add a simple title:
<h1>Welcome to NgBytes Login</h1>
Since pages are routed components, we need to add a route to our auth-routing.module.ts
file that points to the LoginPageComponent
, like so:
import { RouterModule, Routes } from '@angular/router';
import { LoginPageComponent } from './pages/login-page/login-page.component';
import { NgModule } from '@angular/core';
const routes: Routes = [{ path: '', component: LoginPageComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AuthRoutingModule {}
Routing the LoginPageComponent in the AuthRoutingModule
Let’s run the npm start
script, and check that everything is routed correctly. If you open up your browser to localhost:4200
, you should be seeing this:
LoginPageComponent preview
Awesome! It’s time to create a login form, so that our users can sign into our app:
Now we’ll create a login-form
component, inside our auth
directory:
mkdir components
cd components
ng g c login-form
Tip: Since we won’t be writing any tests in this tutorial, feel free to delete the login-form.component.spec.ts
file.
To create our form, we’ll be using the Reactive Forms module. We’ll also be using the Angular Material modules, to style our form. Since it isn’t the goal of this tutorial, I won’t go into any details about how to use reactive forms.
First, we’ll import the ReactiveFormsModule
, the MatInputModule
, the MatFormFieldModule
and the MatButtonModule
modules into our AuthModule
:
import { AuthRoutingModule } from './auth-routing.module';
import { CommonModule } from '@angular/common';
import { LoginFormComponent } from './components/login-form/login-form.component';
import { LoginPageComponent } from './pages/login-page/login-page.component';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [LoginPageComponent, LoginFormComponent],
imports: [
CommonModule,
AuthRoutingModule,
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
MatButtonModule,
],
})
export class AuthModule {}
Importing modules into the AuthModule
Then, we’ll create the form in our login-form.component.ts
file:
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'ngbytes-login-form',
templateUrl: './login-form.component.html',
styleUrls: ['./login-form.component.css'],
})
export class LoginFormComponent implements OnInit {
@Output() formData: EventEmitter<{
email: string;
password: string;
}> = new EventEmitter();
form: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
this.form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', Validators.required],
});
}
get email() {
return this.form.get('email');
}
get password() {
return this.form.get('password');
}
onSubmit() {
this.formData.emit(this.form.value);
}
}
The LoginFormComponent logic
As you can see, we’ve added basic validation to our form: both the email and password fields are required, and the email field only allows valid email addresses.
Tip: Find it odd that we’re using an EventEmitter
to emit our form’s data upon submission? This is because we’re using the smart/dumb component pattern, which decouples business logic from purely presentational components.
Lastly, in our login-form.component.html
file:
<form class="form" [formGroup]="form" (ngSubmit)="onSubmit()">
<mat-form-field class="form-control">
<mat-label>Email</mat-label>
<input matInput formControlName="email" type="text" required />
<mat-error *ngIf="email?.hasError('required')">
Email is required
</mat-error>
<mat-error *ngIf="email?.hasError('email')">
Email isn't valid. Please enter a valid email.
</mat-error>
</mat-form-field>
<mat-form-field class="form-control">
<mat-label>Password</mat-label>
<input matInput formControlName="password" type="password" required />
<mat-error *ngIf="password?.hasError('required')">
Password is required
</mat-error>
</mat-form-field>
<div class="form-footer">
<button [disabled]="form.invalid" mat-raised-button type="submit">
Submit
</button>
</div>
</form>
The LoginFormComponent template
Note: See how we’re disabling the submit button if the form isn’t valid? That’s a bad practice (poor UX), and the only reason I’m doing it is for the sake of brevity, since this tutorial should be focused on AngularFire and not on best practices in Angular forms. However, be warned, don’t do this at home folks!
Let’s add a little CSS in our login-form.component.css
file to make the form prettier (you can skip this step if you wish):
button {
background-color: #5eccd6;
border-radius: 20px;
color: #fff;
padding: 0 50px;
}
form {
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-evenly;
height: 200px;
width: 300px;
}
mat-form-field {
width: 100%;
}
The LoginFormComponent CSS
Great! We now have a lovely login form. All that’s left is to add it to to the login-page.component.html
template:
<div class="form-container">
<h1>Welcome to NgBytes Login</h1>
<h5>Sign in with your email</h5>
<ngbytes-login-form (formData)="login($event)"></ngbytes-login-form>
</div>
Adding the login form to the login page
We also need to inject the authService
we created earlier into our login-page.component.ts
file and create a login
method, which will use the authService
to log the user in, redirecting to the dashboard once it’s done. Don’t forget to catch any possible errors!
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/core/services/auth.service';
import { LoginData } from 'src/app/core/interfaces/login-data.interface';
import { Router } from '@angular/router';
@Component({
selector: 'app-login-page',
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.css'],
})
export class LoginPageComponent implements OnInit {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit(): void {}
login(loginData: LoginData) {
this.authService
.login(loginData)
.then(() => this.router.navigate(['/dashboard']))
.catch((e) => console.log(e.message));
}
}
Creating a login method in the login-page.component
Let’s test what we’ve done so far by going over to our browser, and filling in the form. As you can see, if we try to leave the email field blank, or to enter an invalid email, an error will be shown, which means our validation is working splendidly. However, if we try to press the submit button…
We’re getting a User not found error in our console. Well, of course we are. We haven’t registered any users yet, have we? Let’s create a register form so that we can do so!
Just like we did for our login page, we’re going to create a register page, which will contain the register form. Inside the auth/pages
directory:
ng g c register-page
Let’s add a title to our register page, and some styles:
<div class="container">
<h1>Welcome to NgBytes Register</h1>
</div>
The RegisterPageComponent template
h1 {
color: #838383;
margin-bottom: 50px;
}
.container {
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
margin-top: 80px;
}
The RegisterPageComponent CSS https://gist.github.com/NyaGarcia/f6f3f5892f711fa31fa4acac1c886968
Last, but not least, we need to add a register
route to the auth-routing.module.ts
file:
import { RouterModule, Routes } from '@angular/router';
import { LoginPageComponent } from './pages/login-page/login-page.component';
import { NgModule } from '@angular/core';
import { RegisterPageComponent } from './pages/register-page/register-page.component';
const routes: Routes = [
{ path: '', component: LoginPageComponent },
{ path: 'register', component: RegisterPageComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AuthRoutingModule {}
Adding the RegisterPageComponent route
Since our register page is done, all that we need to do is create a register form… Or do we?
Our login form has two fields, an email and a password field. Our register form needs to have two fields, an email and a password field. See where I’m getting at? Our two forms are going to be exactly the same! You may be thinking that no, they aren’t the same. One form is used to log in, the other to register a new user; two completely different pieces of logic.
However…
Remember how we used the smart/dumb component pattern to decouple the login form, from the login page? Our login form isn’t in charge of performing any logic at all, our login page is. Which means we are free to reuse the login form in our register page:
<div class="container">
<h1>Welcome to NgBytes Register</h1>
<ngbytes-login-form (formData)="register($event)"></ngbytes-login-form>
</div>
Adding the login form to the register page
Neat, isn’t it? Now comes the time to implement the logic to register a new user. We will do so, of course, in our register-page.component.ts
file:
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/core/services/auth.service';
import { LoginData } from 'src/app/core/interfaces/login-data.interface';
import { Router } from '@angular/router';
@Component({
selector: 'app-register-page',
templateUrl: './register-page.component.html',
styleUrls: ['./register-page.component.css'],
})
export class RegisterPageComponent implements OnInit {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit(): void {}
register(data: LoginData) {
this.authService
.register(data)
.then(() => this.router.navigate(['/login']))
.catch((e) => console.log(e.message));
}
}
Adding the register logic to the RegisterPageComponent
As you can see, we’re calling the authService.register
method, which will register the new user in our Firebase app, and then we’re navigating to the login page, so that our newly registered user can log into our app.
Let’s add some final touches to the login page: a button that will redirect new users to the register page, and change the layout a bit to make it look decent:
<div class="background">
<div class="login-container">
<div class="text-container">
<h1>Hey Stranger</h1>
<p>
Want to create an awesome account to access this awesome app? Go ahead
and click the button below!
</p>
<button mat-button routerLink="/register">Sign up</button>
</div>
<div class="form-container">
<h1>Welcome to NgBytes Login</h1>
<h5>Sign in with your email</h5>
<ngbytes-login-form (formData)="login($event)"></ngbytes-login-form>
</div>
</div>
</div>
Improving the login page
Some CSS magic:
a {
color: #838383;
margin-top: 30px;
text-decoration: none;
}
h1 {
color: #5eccd6;
font-weight: bolder;
margin-bottom: 20px;
}
h5 {
color: #838383;
}
button {
border-radius: 20px;
padding: 0 50px;
}
p {
margin-bottom: 20px;
text-align: center;
}
.background {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
}
.login-container {
border-radius: 10px;
box-shadow: 0 0 20px 2px #e4e4e4;
display: flex;
width: 60%;
}
.text-container {
align-items: center;
background-color: #5eccd6;
border-radius: 10px 0 0 10px;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
padding: 50px;
width: 30%;
}
.form-container {
align-items: center;
background-color: #fff;
border-radius: 0 10px 10px 0;
display: flex;
flex-direction: column;
justify-content: center;
padding: 80px;
width: 70%;
}
.text-container h1 {
color: #fff;
}
.text-container button {
background-color: transparent;
border: 1px solid #fff;
}
Adding styles to the login page
Since we’ve made our login page look pretty, let’s take a moment and do the same for our register page:
<div class="background">
<div class="login-container">
<div class="form-container">
<h1>Welcome to NgBytes Register</h1>
<h5>Register with your email</h5>
<ngbytes-login-form (formData)="register($event)"></ngbytes-login-form>
</div>
<div class="text-container">
<h1>Hey Buddy</h1>
<p>
Already have an awesome account? Go ahead and click the button below to
sign in!
</p>
<button mat-button routerLink="/">Sign in</button>
</div>
</div>
</div>
Improving the register page
Some more CSS:
a {
color: #838383;
margin-top: 30px;
text-decoration: none;
}
h1 {
color: #5eccd6;
font-weight: bolder;
margin-bottom: 20px;
}
h5 {
color: #838383;
}
button {
border-radius: 20px;
padding: 0 50px;
}
p {
margin-bottom: 20px;
text-align: center;
}
.background {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
}
.login-container {
border-radius: 10px;
box-shadow: 0 0 20px 2px #e4e4e4;
display: flex;
width: 60%;
}
.text-container {
align-items: center;
background-color: #5eccd6;
border-radius: 0 10px 10px 0;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
padding: 50px;
width: 30%;
}
.form-container {
align-items: center;
background-color: #fff;
border-radius: 10px 0 0 10px;
display: flex;
flex-direction: column;
justify-content: center;
padding: 80px;
width: 70%;
}
.text-container h1 {
color: #fff;
}
.text-container button {
background-color: transparent;
border: 1px solid #fff;
}
Adding styles to the register page
It’s time to test what we’ve done so far. Head over to your browser:
Registering a new user and logging in
Awesome! We’re logging into our app! However, we’re still missing something, aren’t we? We need a logout option! Let’s add it to our dashboard.
Let’s head over to our dashboard.component.html
and create a logout option:
<mat-toolbar><a (click)="logout()">Log out</a></mat-toolbar>
<div class="background">
<h1>Welcome to the NgBytes Dashboard</h1>
<h2>Built with <3 by Dottech</h2>
<a
target="_blank"
class="btn"
href="https://github.com/puntotech/ngbytes-fireauth"
>
Click here to view the source code
</a>
</div>
Adding a logout option to the dashboard
Now that we have our logout option, we need to add some logic to our dashboard.component.ts
. First, we’ll inject the AuthService
in the constructor, and then we’ll create a logout
method:
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../core/services/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
})
export class DashboardComponent implements OnInit {
constructor(private authService: AuthService, private router: Router) {}
ngOnInit(): void {}
logout() {
this.authService
.logout()
.then(() => this.router.navigate(['/']))
.catch((e) => console.log(e.message));
}
}
Adding a logout method to the dashboard component
As you can see, once we call the logout
method, we’ll redirect the user to the default route. That’s it! We’ve successfully implemented a way for our users to log out of our app. Once again, let’s test our app:
Logging out
Adding Google Sign In
Now that our traditional login has been fully implemented, it’s time to add our Google social login. To do so, we’ll first create the necessary method in the auth.service
:
import {
Auth,
GoogleAuthProvider,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signInWithPopup,
signOut,
} from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { LoginData } from '../interfaces/login-data.interface';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: LoginData) {
return signInWithEmailAndPassword(this.auth, email, password);
}
loginWithGoogle() {
return signInWithPopup(this.auth, new GoogleAuthProvider());
}
register({ email, password }: LoginData) {
return createUserWithEmailAndPassword(this.auth, email, password);
}
logout() {
return signOut(this.auth);
}
}
Adding a loginWithGoogle method to the AuthService
Then, we’ll need to add a Google sign in button to the login page:
<div class="background">
<div class="login-container">
<div class="text-container">
<h1>Hey Stranger</h1>
<p>
Want to create an awesome account to access this awesome app? Go ahead
and click the button below!
</p>
<button mat-button routerLink="/register">Sign up</button>
</div>
<div class="form-container">
<h1>Welcome to NgBytes Login</h1>
<button class="mat-raised-button" (click)="loginWithGoogle()">
Sign In with Google
</button>
<h5>Or sign in with your email</h5>
<ngbytes-login-form (formData)="login($event)"></ngbytes-login-form>
</div>
</div>
</div>
Adding a Google sign in button
Finally, we’ll need to add a loginWithGoogle
function to our login-page.component.ts
:
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/core/services/auth.service';
import { LoginData } from 'src/app/core/interfaces/login-data.interface';
import { Router } from '@angular/router';
@Component({
selector: 'app-login-page',
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.css'],
})
export class LoginPageComponent implements OnInit {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit(): void {}
login(loginData: LoginData) {
this.authService
.login(loginData)
.then(() => this.router.navigate(['/dashboard']))
.catch((e) => console.log(e.message));
}
loginWithGoogle() {
this.authService
.loginWithGoogle()
.then(() => this.router.navigate(['/dashboard']))
.catch((e) => console.log(e.message));
}
}
Adding a loginWithGoogle method to the LoginPageComponent
We’re done! Let’s head over to our browser and try it out:
Signing in with google
Protecting the routes
If you attempt to navigate to the dashboard page localhost:4200/dashboard
, you’ll see that you’ll be able to do so, even if you’re unauthenticated. To solve this problem, we’ll use the auth guard provided by AngularFire, to automatically redirect unauthorized users to the login page. In our app-routing.module.ts
:
import { AuthGuard, redirectUnauthorizedTo } from '@angular/fire/auth-guard';
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['']);
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectUnauthorizedToLogin },
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Protecting the routes
As you can see, we’ve protected the dashboard
route with the AngularFire AuthGuard
, and we’ve built a redirectUnauthorizedToLogin
pipe, using the AngularFire redirectUnauthorizedTo
pipe, which we then provide to the AuthGuard
. This will ensure that any unauthenticated user that tries to access the dashboard page, will automatically be redirected to the login page. Feel free to head over to your browser and try it out.
The same way that we don’t want unauthorized users to access the dashboard, if the user is already authenticated, they shouldn’t be able to navigate to the login page. Let’s create another custom pipe, one that will redirect the already logged in users to the dashboard:
import {
AuthGuard,
redirectLoggedInTo,
redirectUnauthorizedTo,
} from '@angular/fire/auth-guard';
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['']);
const redirectLoggedInToHome = () => redirectLoggedInTo(['dashboard']);
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectLoggedInToHome },
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectUnauthorizedToLogin },
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Adding the redirectLoggedInToHome route guard
Now, if we’re logged in and we try to access the login page, we’ll be automatically redirected to the dashboard.
AngularFire provides us with a canActivate
helper, which we can use together with the spread operator, to make our routes more readable:
import { RouterModule, Routes } from '@angular/router';
import {
canActivate,
redirectLoggedInTo,
redirectUnauthorizedTo,
} from '@angular/fire/auth-guard';
import { NgModule } from '@angular/core';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['']);
const redirectLoggedInToHome = () => redirectLoggedInTo(['dashboard']);
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
...canActivate(redirectLoggedInToHome),
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
...canActivate(redirectUnauthorizedToLogin),
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Using the canActivate helper to make the routes more readable
Conclusion
That’s all folks! Congratulations, you’ve successfully created an authentication application with Angular 13 and the latest AngularFire 7 modular SDK.
Please remember that the new API isn’t complete, so if you decide to go ahead anyway and use it in production, do so at your own risk. Also, bear in mind that AngularFire 7 provides a compatibility layer so that you can continue using the previous AngularFire 6 API.
I hope you found this tutorial useful. Thank you very much for reading.
This story was originally published at https://betterprogramming.pub/angular-13-firebase-authentication-tutorial-with-angularfire-7-23dc8cee42c4
1654428300
La autenticación es clave para proteger nuestras aplicaciones de usuarios no registrados. En este tutorial, aprenderá a implementar tanto un inicio de sesión de correo electrónico/contraseña tradicional como un inicio de sesión social de Google en Firebase, con el nuevo SDK modular AngularFire v.7.0 que se puede sacudir en árbol, lo que nos permite aprovechar al máximo el nuevo árbol. SDK de Firebase JS movible (v9) .
También aprenderá cómo proteger las rutas con AngularFire Auth Guard y cómo redirigir a los usuarios a ciertas páginas en función de si están autenticados o no.
Tenga en cuenta que, al momento de escribir este tutorial, la API de Angular Fire v.7.0 todavía está en desarrollo y no tiene la función completa ; y los documentos aún no están actualizados a la última versión. AngularFire proporciona una capa de compatibilidad que le permitirá utilizar la última versión de la biblioteca, al mismo tiempo que es totalmente compatible con la API de AngularFire v6.0. Dicho esto, si tienes tanta curiosidad como yo y disfrutas probando las últimas versiones de todas tus bibliotecas, sigue adelante y disfruta de este tutorial.
Aquí hay una vista previa de la aplicación que construiremos hoy:
Vista previa de la aplicación de autenticación
Configuración del proyecto de Firebase
Para crear un nuevo proyecto de Firebase, debemos iniciar sesión en la consola de Firebase. Una vez que hayamos hecho eso, podemos hacer clic en Agregar proyecto para crear un nuevo proyecto:
Firebase — Creando un nuevo proyecto
Tendremos que elegir un nombre para nuestro proyecto:
Firebase: elegir un nombre
Decida si queremos o no Google Analytics en nuestro proyecto (usted elige, me quedo con no):
Firebase: habilitar Google Analytics
¡Hemos terminado! Ahora tenemos un hermoso proyecto nuevo de Firebase.
Ahora necesitamos agregar Firebase a nuestra aplicación Angular. Para hacerlo, debemos hacer clic en el botón de la aplicación web, en el tablero:
Firebase — Agregar una aplicación web
Luego tenemos que elegir un nombre:
Finalmente, ahora tenemos la configuración que se usará más adelante para agregar Firebase a nuestra aplicación Angular:
Es hora de agregar autenticación y elegir los métodos de inicio de sesión que queremos implementar. En este tutorial, agregaremos un inicio de sesión social de Google y un inicio de sesión de correo electrónico/contraseña tradicional. Dirígete a la página de Autenticación y elige los proveedores:
Firebase: elegir métodos de inicio de sesión
Una vez que hayamos habilitado ambos métodos de inicio de sesión, nuestro panel de Autenticación debería verse así:
Firebase: métodos de inicio de sesión habilitados
¡Eso es todo! Ahora tenemos nuestra aplicación Firebase completamente configurada y lista. Es hora de comenzar con nuestra aplicación Angular.
Configuración de un proyecto angular
Lo primero que vamos a hacer es crear un nuevo proyecto con Angular CLI.
Sugerencia: si no ha instalado Angular CLI, puede hacerlo ejecutando el siguiente comando:
npm i -g @angular/cli
Para crear un nuevo proyecto Angular, podemos ejecutar el siguiente comando:
ng new ngbytes-fireauth
Nota: ¡No olvide responder sí cuando se le pregunte si desea agregar enrutamiento a su nueva aplicación!
Una vez que la CLI ha hecho su magia, podemos abrir el proyecto recién creado con nuestro IDE favorito (sugiero VSCode , que es el que normalmente uso).
Agreguemos Firebase y Angularfire a nuestro proyecto. Para ello, utilizaremos el esquema de AngularFire, que se encargará de configurarlo todo por nosotros. Ejecutemos el siguiente comando:
ng add @angular/fire
Se nos hará una serie de preguntas, como qué funciones de Firebase nos gustaría configurar. Para este tutorial, solo necesitamos agregar autenticación, así que seleccionemos eso:
Luego se nos preguntará sobre la cuenta de Firebase que nos gustaría usar y qué proyecto queremos configurar. Seleccione el proyecto que creamos anteriormente y luego seleccione la aplicación que también creamos anteriormente.
Una vez que hayamos hecho todo esto, verá que el esquema se ha ocupado de toda la configuración de Firebase por nosotros. ¡Impresionante!
También agregaremos material angular. Una vez más, usaremos un esquema:
ng add @angular/material
Tendremos que establecer la strictPropertyInitialization
propiedad en el tsconfig.json
archivo en falso. Estamos haciendo esto porque el modo estricto está habilitado de manera predeterminada en todas las nuevas aplicaciones de Angular a partir de la versión 12, lo que significa que TypeScript se quejará si declaramos propiedades de clase sin configurarlas en el constructor (una práctica común en Angular). tsconfig.json
Así es como debería verse nuestro archivo:
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"strictPropertyInitialization": false,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2017",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}
archivo tsconfig.json
Por último, pero no menos importante, eliminaremos el código repetitivo que Angular genera automáticamente en el archivo app.component.html
. Tenga mucho cuidado y asegúrese de no eliminar las <router-outlet></router-outlet>
etiquetas cuando elimine el código repetitivo, o el enrutador no funcionará.
Después de eliminar todo, app.component.html
solo debe contener las etiquetas de salida del enrutador:
etiquetas de salida de enrutador en app.component.html
Creando el tablero
Es hora de comenzar a construir nuestra aplicación Angular. Comencemos por crear un dashboard
módulo. Este es el módulo que contendrá las páginas que queremos proteger de usuarios no autenticados. Comencemos por crear un features
directorio dentro del src/app
directorio, que contendrá todos nuestros módulos de características:
mkdir features
Dentro de este nuevo directorio, crearemos nuestro dashboard
módulo con Angular CLI:
ng g m dashboard -m app --route dashboard
Sugerencia: estamos usando la --route
opción, que crea un componente en el nuevo módulo y agrega la ruta a ese componente en la matriz declarada en el módulo provisto en la opción.Routes
-m
Verás que la CLI ha creado un dashboard
módulo dentro del features
directorio, con su dashboard-routing.module.ts
componente correspondiente. También modificó el app-routing.module.ts
, y agregó una dashboard
ruta, lazy loading el DashboardModule
.
Agreguemos información a nuestro dashboard
componente, modificando el dashboard.component.html
archivo:
<mat-toolbar></mat-toolbar>
<div class="background">
<h1>Welcome to the NgBytes Dashboard</h1>
<h2>Built with <3 by Dottech</h2>
<a target="_blank" class="btn" href="https://github.com/puntotech/ngbytes-fireauth">
Click here to view the source code
</a>
</div>
La plantilla del componente del tablero
Siéntete libre de agregar lo que quieras a este archivo.
Sugerencia: no olvides importar el MatToolbarModule
al DashboardModule
, si decides usarlo también.
Agreguemos un poco de CSS para que el tablero se vea bonito:
h1 {
color: #5eccd6;
font-size: 50px;
font-weight: bolder;
}
h2 {
color: #698893;
margin-bottom: 50px;
}
a {
color: #5eccd6;
border-bottom: 2px solid transparent;
transition: 0.5s border-bottom;
}
mat-toolbar {
background-color: transparent;
display: flex;
justify-content: right;
}
.background {
align-items: center;
background-image: url("/assets/mountains.png");
box-sizing: border-box;
display: flex;
flex-direction: column;
height: calc(100vh - 64px);
padding-top: 10%;
}
.btn {
background-color: #5eccd6;
border-radius: 20px;
color: #fff;
padding: 10px 30px;
text-decoration: none;
transition: 0.5s background-color;
}
a:hover {
border-bottom: 2px solid #5eccd6;
cursor: pointer;
}
.btn:hover {
background-color: #52b7c0;
}
El CSS del componente del tablero
Et voilà, ¡nuestro dashboard
módulo está listo! Para verlo, podemos navegar a http://localhost:4200/dashboard
:
Vista previa del panel
Es hora de comenzar con nuestra autenticación.
Creación del módulo de autenticación
Dentro del features
directorio, usaremos Angular CLI para crear un auth
módulo:
ng g m auth --routing
Sugerencia: estamos usando la --routing
bandera, que genera tanto un auth-routing.module.ts
como elauth.module.ts
.
Una vez que hemos creado nuestro módulo, debemos agregar su ruta correspondiente al app-routing.module.ts
archivo.
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Agregar rutas al módulo de enrutamiento de aplicaciones
Tenga en cuenta que estamos agregando una ruta comodín, que redirige a la ruta predeterminada. Hacemos esto para que, si intentamos navegar a rutas que no existen, en lugar de que se arroje un error, seremos redirigidos.
Todas nuestras interacciones con Firebase y AngularFire se llevarán a cabo dentro del AuthService
. Para crear nuestro servicio, volveremos a utilizar la CLI de Angular. Dentro de nuestro app
directorio:
mkdir core
cd core
mkdir services
cd services
ng g s auth
Dentro de nuestro authService
, lo primero que debemos hacer es inyectar la Auth
instancia de AngularFire en el constructor. Luego, crearemos un login
método, que recibirá los datos de inicio de sesión (correo electrónico y contraseña), y usaremos el signInWithEmailAndPassword
método AngularFire para iniciar sesión:
import { Auth, signInWithEmailAndPassword } from '@angular/fire/auth';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: any) {
return signInWithEmailAndPassword(this.auth, email, password);
}
}
AuthService con método de inicio de sesión sin tipo
Sin embargo, ¿no parece algo extraño en nuestra login
función? Tenemos un muy indecoroso any
! Como estamos usando TypeScript y queremos tener todo bien escrito, creemos una LoginData
interfaz para escribir nuestros datos de inicio de sesión. En nuestro core
directorio:
mkdir interfaces
cd interfaces
touch login-data.interface.ts
Así es como se verá nuestra interfaz:
export interface LoginData {
email: string;
password: string;
}
La interfaz LoginData https://gist.github.com/NyaGarcia/2ec63c5fe25c1d281fcdb011722e9712
Ahora podemos usar nuestra nueva interfaz para escribir correctamente login
los parámetros de nuestra función:
import { Auth, signInWithEmailAndPassword } from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { LoginData } from '../interfaces/login-data.interface';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: LoginData) {
return signInWithEmailAndPassword(this.auth, email, password);
}
}
AuthService con función de inicio de sesión escrita
Se ve mucho mejor, ¿no?
También crearemos un método de registro, que usará el createUserWithEmailAndPassword
método AngularFire, para que los nuevos usuarios puedan registrarse en nuestra aplicación. También crearemos un logout
método que, como probablemente pueda adivinar, permitirá a nuestros usuarios cerrar sesión en nuestra aplicación:
import {
Auth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
} from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { LoginData } from '../interfaces/login-data.interface';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: LoginData) {
return signInWithEmailAndPassword(this.auth, email, password);
}
register({ email, password }: LoginData) {
return createUserWithEmailAndPassword(this.auth, email, password);
}
logout() {
return signOut(this.auth);
}
}
Agregar las funciones de registro y cierre de sesión al AuthService
Eso es todo lo que tenemos que hacer en nuestro AuthService
(agregaremos la opción de inicio de sesión social de Google más adelante) por ahora.
Ahora es el momento de crear los formularios de registro e inicio de sesión, para que nuestros usuarios puedan autenticarse.
Dentro del auth
directorio, crearemos un login-page
componente, que mostrará un mensaje de bienvenida, el formulario de inicio de sesión para que los usuarios puedan iniciar sesión y, más adelante, el botón de inicio de sesión de Google:
mkdir pages
cd pages
ng g c login-page
Una vez que hayamos creado nuestra página, vayamos al login-page.component.html
archivo y agreguemos un título simple:
<h1>Welcome to NgBytes Login</h1>
Dado que las páginas son componentes enrutados, debemos agregar una ruta a nuestro auth-routing.module.ts
archivo que apunte al LoginPageComponent
, así:
import { RouterModule, Routes } from '@angular/router';
import { LoginPageComponent } from './pages/login-page/login-page.component';
import { NgModule } from '@angular/core';
const routes: Routes = [{ path: '', component: LoginPageComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AuthRoutingModule {}
Enrutamiento de LoginPageComponent en AuthRoutingModule
Ejecutemos el npm start
script y verifiquemos que todo esté enrutado correctamente. Si abre su navegador a localhost:4200
, debería estar viendo esto:
Vista previa de LoginPageComponent
¡Impresionante! Es hora de crear un formulario de inicio de sesión, para que nuestros usuarios puedan iniciar sesión en nuestra aplicación:
Ahora crearemos un login-form
componente, dentro de nuestro auth
directorio:
mkdir components
cd components
ng g c login-form
Sugerencia: dado que no escribiremos ninguna prueba en este tutorial, no dude en eliminar el login-form.component.spec.ts
archivo.
Para crear nuestro formulario, usaremos el módulo Formularios reactivos. También usaremos los módulos de material angular para diseñar nuestro formulario. Dado que no es el objetivo de este tutorial, no entraré en detalles sobre cómo usar formularios reactivos.
Primero, importaremos los módulos ReactiveFormsModule
, the MatInputModule
, the MatFormFieldModule
y the en nuestro :MatButtonModuleAuthModule
import { AuthRoutingModule } from './auth-routing.module';
import { CommonModule } from '@angular/common';
import { LoginFormComponent } from './components/login-form/login-form.component';
import { LoginPageComponent } from './pages/login-page/login-page.component';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [LoginPageComponent, LoginFormComponent],
imports: [
CommonModule,
AuthRoutingModule,
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
MatButtonModule,
],
})
export class AuthModule {}
Importación de módulos en AuthModule
Luego, crearemos el formulario en nuestro login-form.component.ts
archivo:
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'ngbytes-login-form',
templateUrl: './login-form.component.html',
styleUrls: ['./login-form.component.css'],
})
export class LoginFormComponent implements OnInit {
@Output() formData: EventEmitter<{
email: string;
password: string;
}> = new EventEmitter();
form: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
this.form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', Validators.required],
});
}
get email() {
return this.form.get('email');
}
get password() {
return this.form.get('password');
}
onSubmit() {
this.formData.emit(this.form.value);
}
}
La lógica de LoginFormComponent
Como puede ver, hemos agregado una validación básica a nuestro formulario: tanto el campo de correo electrónico como el de contraseña son obligatorios, y el campo de correo electrónico solo permite direcciones de correo electrónico válidas.
Sugerencia: ¿Le parece extraño que estemos usando un EventEmitter
correo electrónico para emitir los datos de nuestro formulario al enviarlo? Esto se debe a que estamos usando el patrón de componente inteligente/tonto, que desvincula la lógica comercial de los componentes puramente de presentación.
Por último, en nuestro login-form.component.html
archivo:
<form class="form" [formGroup]="form" (ngSubmit)="onSubmit()">
<mat-form-field class="form-control">
<mat-label>Email</mat-label>
<input matInput formControlName="email" type="text" required />
<mat-error *ngIf="email?.hasError('required')">
Email is required
</mat-error>
<mat-error *ngIf="email?.hasError('email')">
Email isn't valid. Please enter a valid email.
</mat-error>
</mat-form-field>
<mat-form-field class="form-control">
<mat-label>Password</mat-label>
<input matInput formControlName="password" type="password" required />
<mat-error *ngIf="password?.hasError('required')">
Password is required
</mat-error>
</mat-form-field>
<div class="form-footer">
<button [disabled]="form.invalid" mat-raised-button type="submit">
Submit
</button>
</div>
</form>
La plantilla LoginFormComponent
Nota: ¿Ves cómo estamos deshabilitando el botón de enviar si el formulario no es válido? Esa es una mala práctica (UX deficiente), y la única razón por la que lo hago es por brevedad, ya que este tutorial debe centrarse en AngularFire y no en las mejores prácticas en formularios Angular. Sin embargo, tenga cuidado, ¡no haga esto en casa, amigos!
Agreguemos un poco de CSS en nuestro login-form.component.css
archivo para que el formulario sea más bonito (puede omitir este paso si lo desea):
button {
background-color: #5eccd6;
border-radius: 20px;
color: #fff;
padding: 0 50px;
}
form {
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-evenly;
height: 200px;
width: 300px;
}
mat-form-field {
width: 100%;
}
El CSS del componente LoginForm
¡Excelente! Ahora tenemos un hermoso formulario de inicio de sesión. Todo lo que queda es agregarlo a la login-page.component.html
plantilla:
<div class="form-container">
<h1>Welcome to NgBytes Login</h1>
<h5>Sign in with your email</h5>
<ngbytes-login-form (formData)="login($event)"></ngbytes-login-form>
</div>
Adición del formulario de inicio de sesión a la página de inicio de sesión
También necesitamos inyectar lo authService
que creamos anteriormente en nuestro login-page.component.ts
archivo y crear un login
método, que usará authService
para iniciar sesión en el usuario, redirigiéndolo al tablero una vez que haya terminado. ¡No olvides detectar cualquier posible error!
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/core/services/auth.service';
import { LoginData } from 'src/app/core/interfaces/login-data.interface';
import { Router } from '@angular/router';
@Component({
selector: 'app-login-page',
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.css'],
})
export class LoginPageComponent implements OnInit {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit(): void {}
login(loginData: LoginData) {
this.authService
.login(loginData)
.then(() => this.router.navigate(['/dashboard']))
.catch((e) => console.log(e.message));
}
}
Creación de un método de inicio de sesión en login-page.component
Probemos lo que hemos hecho hasta ahora yendo a nuestro navegador y completando el formulario. Como puede ver, si intentamos dejar el campo de correo electrónico en blanco, o ingresar un correo electrónico no válido, se mostrará un error, lo que significa que nuestra validación está funcionando espléndidamente. Sin embargo, si intentamos presionar el botón de enviar…
Estamos recibiendo un error de Usuario no encontrado en nuestra consola. Bueno, por supuesto que lo somos. Todavía no hemos registrado ningún usuario, ¿verdad? ¡Creemos un formulario de registro para que podamos hacerlo!
Al igual que hicimos con nuestra página de inicio de sesión, vamos a crear una página de registro, que contendrá el formulario de registro. Dentro del auth/pages
directorio:
ng g c register-page
Agreguemos un título a nuestra página de registro y algunos estilos:
<div class="container">
<h1>Welcome to NgBytes Register</h1>
</div>
La plantilla RegisterPageComponent
h1 {
color: #838383;
margin-bottom: 50px;
}
.container {
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
margin-top: 80px;
}
El CSS de RegisterPageComponent https://gist.github.com/NyaGarcia/f6f3f5892f711fa31fa4acac1c886968
Por último, pero no menos importante, debemos agregar una register
ruta al auth-routing.module.ts
archivo:
import { RouterModule, Routes } from '@angular/router';
import { LoginPageComponent } from './pages/login-page/login-page.component';
import { NgModule } from '@angular/core';
import { RegisterPageComponent } from './pages/register-page/register-page.component';
const routes: Routes = [
{ path: '', component: LoginPageComponent },
{ path: 'register', component: RegisterPageComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AuthRoutingModule {}
Agregar la ruta RegisterPageComponent
Ya que nuestra página de registro está lista, todo lo que tenemos que hacer es crear un formulario de registro... ¿o sí?
Nuestro formulario de inicio de sesión tiene dos campos, un correo electrónico y un campo de contraseña. Nuestro formulario de registro debe tener dos campos, un correo electrónico y un campo de contraseña. ¿Ves a dónde estoy llegando? ¡Nuestras dos formas van a ser exactamente iguales! Puede que estés pensando que no, que no son lo mismo. Un formulario se utiliza para iniciar sesión, el otro para registrar un nuevo usuario; dos piezas de lógica completamente diferentes.
Sin embargo…
¿Recuerda cómo usamos el patrón de componente inteligente/tonto para desacoplar el formulario de inicio de sesión de la página de inicio de sesión? Nuestro formulario de inicio de sesión no está a cargo de realizar ninguna lógica en absoluto, nuestra página de inicio de sesión sí lo está. Lo que significa que somos libres de reutilizar el formulario de inicio de sesión en nuestra página de registro:
<div class="container">
<h1>Welcome to NgBytes Register</h1>
<ngbytes-login-form (formData)="register($event)"></ngbytes-login-form>
</div>
Agregar el formulario de inicio de sesión a la página de registro
Genial, ¿no? Ahora llega el momento de implementar la lógica para dar de alta un nuevo usuario. Así lo haremos, por supuesto, en nuestra register-page.component.ts
ficha:
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/core/services/auth.service';
import { LoginData } from 'src/app/core/interfaces/login-data.interface';
import { Router } from '@angular/router';
@Component({
selector: 'app-register-page',
templateUrl: './register-page.component.html',
styleUrls: ['./register-page.component.css'],
})
export class RegisterPageComponent implements OnInit {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit(): void {}
register(data: LoginData) {
this.authService
.register(data)
.then(() => this.router.navigate(['/login']))
.catch((e) => console.log(e.message));
}
}
Agregar la lógica de registro a RegisterPageComponent
Como puede ver, estamos llamando al authService.register
método, que registrará al nuevo usuario en nuestra aplicación Firebase, y luego estamos navegando a la página de inicio de sesión, para que nuestro usuario recién registrado pueda iniciar sesión en nuestra aplicación.
Agreguemos algunos toques finales a la página de inicio de sesión: un botón que redirigirá a los nuevos usuarios a la página de registro y cambiará un poco el diseño para que se vea decente:
<div class="background">
<div class="login-container">
<div class="text-container">
<h1>Hey Stranger</h1>
<p>
Want to create an awesome account to access this awesome app? Go ahead
and click the button below!
</p>
<button mat-button routerLink="/register">Sign up</button>
</div>
<div class="form-container">
<h1>Welcome to NgBytes Login</h1>
<h5>Sign in with your email</h5>
<ngbytes-login-form (formData)="login($event)"></ngbytes-login-form>
</div>
</div>
</div>
Mejorando la página de inicio de sesión
Un poco de magia CSS:
a {
color: #838383;
margin-top: 30px;
text-decoration: none;
}
h1 {
color: #5eccd6;
font-weight: bolder;
margin-bottom: 20px;
}
h5 {
color: #838383;
}
button {
border-radius: 20px;
padding: 0 50px;
}
p {
margin-bottom: 20px;
text-align: center;
}
.background {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
}
.login-container {
border-radius: 10px;
box-shadow: 0 0 20px 2px #e4e4e4;
display: flex;
width: 60%;
}
.text-container {
align-items: center;
background-color: #5eccd6;
border-radius: 10px 0 0 10px;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
padding: 50px;
width: 30%;
}
.form-container {
align-items: center;
background-color: #fff;
border-radius: 0 10px 10px 0;
display: flex;
flex-direction: column;
justify-content: center;
padding: 80px;
width: 70%;
}
.text-container h1 {
color: #fff;
}
.text-container button {
background-color: transparent;
border: 1px solid #fff;
}
Agregar estilos a la página de inicio de sesión
Ya que hemos hecho que nuestra página de inicio de sesión se vea bonita, tomemos un momento y hagamos lo mismo con nuestra página de registro:
<div class="background">
<div class="login-container">
<div class="form-container">
<h1>Welcome to NgBytes Register</h1>
<h5>Register with your email</h5>
<ngbytes-login-form (formData)="register($event)"></ngbytes-login-form>
</div>
<div class="text-container">
<h1>Hey Buddy</h1>
<p>
Already have an awesome account? Go ahead and click the button below to
sign in!
</p>
<button mat-button routerLink="/">Sign in</button>
</div>
</div>
</div>
Mejorando la página de registro
Un poco más de CSS:
a {
color: #838383;
margin-top: 30px;
text-decoration: none;
}
h1 {
color: #5eccd6;
font-weight: bolder;
margin-bottom: 20px;
}
h5 {
color: #838383;
}
button {
border-radius: 20px;
padding: 0 50px;
}
p {
margin-bottom: 20px;
text-align: center;
}
.background {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
}
.login-container {
border-radius: 10px;
box-shadow: 0 0 20px 2px #e4e4e4;
display: flex;
width: 60%;
}
.text-container {
align-items: center;
background-color: #5eccd6;
border-radius: 0 10px 10px 0;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
padding: 50px;
width: 30%;
}
.form-container {
align-items: center;
background-color: #fff;
border-radius: 10px 0 0 10px;
display: flex;
flex-direction: column;
justify-content: center;
padding: 80px;
width: 70%;
}
.text-container h1 {
color: #fff;
}
.text-container button {
background-color: transparent;
border: 1px solid #fff;
}
Agregar estilos a la página de registro
Es hora de probar lo que hemos hecho hasta ahora. Dirígete a tu navegador:
Registro de un nuevo usuario e inicio de sesión
¡Impresionante! ¡Estamos iniciando sesión en nuestra aplicación! Sin embargo, todavía nos falta algo, ¿no? ¡Necesitamos una opción de cierre de sesión! Vamos a agregarlo a nuestro tablero.
Vayamos a nuestro dashboard.component.html
y creemos una opción de cierre de sesión:
<mat-toolbar><a (click)="logout()">Log out</a></mat-toolbar>
<div class="background">
<h1>Welcome to the NgBytes Dashboard</h1>
<h2>Built with <3 by Dottech</h2>
<a
target="_blank"
class="btn"
href="https://github.com/puntotech/ngbytes-fireauth"
>
Click here to view the source code
</a>
</div>
Agregar una opción de cierre de sesión al tablero
Ahora que tenemos nuestra opción de cierre de sesión, necesitamos agregar algo de lógica a nuestro archivo dashboard.component.ts
. Primero, inyectaremos el AuthService
en el constructor y luego crearemos un logout
método:
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../core/services/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
})
export class DashboardComponent implements OnInit {
constructor(private authService: AuthService, private router: Router) {}
ngOnInit(): void {}
logout() {
this.authService
.logout()
.then(() => this.router.navigate(['/']))
.catch((e) => console.log(e.message));
}
}
Agregar un método de cierre de sesión al componente del tablero
Como puede ver, una vez que llamemos al logout
método, redirigiremos al usuario a la ruta predeterminada. ¡Eso es todo! Hemos implementado con éxito una forma para que nuestros usuarios cierren sesión en nuestra aplicación. Una vez más, probemos nuestra aplicación:
Saliendo de tu cuenta
Adición de inicio de sesión de Google
Ahora que nuestro inicio de sesión tradicional se ha implementado por completo, es hora de agregar nuestro inicio de sesión social de Google. Para hacerlo, primero crearemos el método necesario en el auth.service
:
import {
Auth,
GoogleAuthProvider,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signInWithPopup,
signOut,
} from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { LoginData } from '../interfaces/login-data.interface';
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private auth: Auth) {}
login({ email, password }: LoginData) {
return signInWithEmailAndPassword(this.auth, email, password);
}
loginWithGoogle() {
return signInWithPopup(this.auth, new GoogleAuthProvider());
}
register({ email, password }: LoginData) {
return createUserWithEmailAndPassword(this.auth, email, password);
}
logout() {
return signOut(this.auth);
}
}
Agregar un método loginWithGoogle al AuthService
Luego, necesitaremos agregar un botón de inicio de sesión de Google a la página de inicio de sesión:
<div class="background">
<div class="login-container">
<div class="text-container">
<h1>Hey Stranger</h1>
<p>
Want to create an awesome account to access this awesome app? Go ahead
and click the button below!
</p>
<button mat-button routerLink="/register">Sign up</button>
</div>
<div class="form-container">
<h1>Welcome to NgBytes Login</h1>
<button class="mat-raised-button" (click)="loginWithGoogle()">
Sign In with Google
</button>
<h5>Or sign in with your email</h5>
<ngbytes-login-form (formData)="login($event)"></ngbytes-login-form>
</div>
</div>
</div>
Agregar un botón de inicio de sesión de Google
Finalmente, necesitaremos agregar una loginWithGoogle
función a nuestro login-page.component.ts
:
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/core/services/auth.service';
import { LoginData } from 'src/app/core/interfaces/login-data.interface';
import { Router } from '@angular/router';
@Component({
selector: 'app-login-page',
templateUrl: './login-page.component.html',
styleUrls: ['./login-page.component.css'],
})
export class LoginPageComponent implements OnInit {
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit(): void {}
login(loginData: LoginData) {
this.authService
.login(loginData)
.then(() => this.router.navigate(['/dashboard']))
.catch((e) => console.log(e.message));
}
loginWithGoogle() {
this.authService
.loginWithGoogle()
.then(() => this.router.navigate(['/dashboard']))
.catch((e) => console.log(e.message));
}
}
Agregar un método loginWithGoogle al LoginPageComponent
¡Hemos terminado! Vayamos a nuestro navegador y pruébelo:
Iniciar sesión con google
Protegiendo las rutas
Si intenta navegar a la página del panel localhost:4200/dashboard
, verá que podrá hacerlo, incluso si no está autenticado. Para resolver este problema, utilizaremos la protección de autenticación proporcionada por AngularFire para redirigir automáticamente a los usuarios no autorizados a la página de inicio de sesión. En nuestro app-routing.module.ts
:
import { AuthGuard, redirectUnauthorizedTo } from '@angular/fire/auth-guard';
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['']);
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectUnauthorizedToLogin },
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Protegiendo las rutas
Como puede ver, hemos protegido la dashboard
ruta con AngularFire AuthGuard
y hemos construido una redirectUnauthorizedToLogin
canalización, utilizando la redirectUnauthorizedTo
canalización AngularFire, que luego proporcionamos a AuthGuard
. Esto garantizará que cualquier usuario no autenticado que intente acceder a la página del panel de control será redirigido automáticamente a la página de inicio de sesión. Siéntase libre de dirigirse a su navegador y probarlo.
De la misma manera que no queremos que los usuarios no autorizados accedan al panel, si el usuario ya está autenticado, no debería poder navegar a la página de inicio de sesión. Vamos a crear otra canalización personalizada, una que redirigirá a los usuarios que ya iniciaron sesión al tablero:
import {
AuthGuard,
redirectLoggedInTo,
redirectUnauthorizedTo,
} from '@angular/fire/auth-guard';
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['']);
const redirectLoggedInToHome = () => redirectLoggedInTo(['dashboard']);
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectLoggedInToHome },
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
canActivate: [AuthGuard],
data: { authGuardPipe: redirectUnauthorizedToLogin },
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Agregar el protector de ruta redirectLoggedInToHome
Ahora, si hemos iniciado sesión e intentamos acceder a la página de inicio de sesión, seremos redirigidos automáticamente al panel de control.
AngularFire nos proporciona un canActivate
asistente, que podemos usar junto con el operador de propagación, para hacer que nuestras rutas sean más legibles:
import { RouterModule, Routes } from '@angular/router';
import {
canActivate,
redirectLoggedInTo,
redirectUnauthorizedTo,
} from '@angular/fire/auth-guard';
import { NgModule } from '@angular/core';
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['']);
const redirectLoggedInToHome = () => redirectLoggedInTo(['dashboard']);
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/auth/auth.module').then((m) => m.AuthModule),
...canActivate(redirectLoggedInToHome),
},
{
path: 'dashboard',
loadChildren: () =>
import('./features/dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
...canActivate(redirectUnauthorizedToLogin),
},
{
path: '**',
redirectTo: '',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Usando el ayudante canActivate para hacer las rutas más legibles
Conclusión
¡Eso es todo amigos! Enhorabuena, ha creado correctamente una aplicación de autenticación con Angular 13 y el último SDK modular de AngularFire 7.
Recuerde que la nueva API no está completa, por lo que si decide seguir adelante de todos modos y usarla en producción, hágalo bajo su propio riesgo. Además, tenga en cuenta que AngularFire 7 proporciona una capa de compatibilidad para que pueda continuar usando la API anterior de AngularFire 6.
Espero que hayas encontrado útil este tutorial. Muchas gracias por leer.
Esta historia se publicó originalmente en https://betterprogramming.pub/angular-13-firebase-authentication-tutorial-with-angularfire-7-23dc8cee42c4
1592807820
What is 2FA
Two-Factor Authentication (or 2FA as it often referred to) is an extra layer of security that is used to provide users an additional level of protection when securing access to an account.
Employing a 2FA mechanism is a vast improvement in security over the Singe-Factor Authentication method of simply employing a username and password. Using this method, accounts that have 2FA enabled, require the user to enter a one-time passcode that is generated by an external application. The 2FA passcode (usually a six-digit number) is required to be input into the passcode field before access is granted. The 2FA input is usually required directly after the username and password are entered by the client.
#tutorials #2fa #access #account security #authentication #authentication method #authentication token #cli #command line #cpanel #feature manager #google authenticator #one time password #otp #otp authentication #passcode #password #passwords #qr code #security #security code #security policy #security practices #single factor authentication #time-based one-time password #totp #two factor authentication #whm
1597017248
#firebase #firebase database #c# with firebase #c# with firebase tutorials #c# with firebase database #asp.net with firebase database