mirror of
https://github.com/akveo/ngx-admin.git
synced 2025-12-16 23:40:14 +01:00
Firebase authentication completed
This commit is contained in:
parent
b1e895f69a
commit
c26e0c99f1
11 changed files with 277 additions and 19 deletions
|
|
@ -1,9 +1,11 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { ExtraOptions, RouterModule, Routes } from '@angular/router';
|
||||
import { AuthGuard } from './service/auth-guard.service';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: 'pages',
|
||||
canActivate: [AuthGuard],
|
||||
loadChildren: () => import('./pages/pages.module')
|
||||
.then(m => m.PagesModule),
|
||||
},
|
||||
|
|
@ -11,7 +13,6 @@ export const routes: Routes = [
|
|||
path: 'auth',
|
||||
loadChildren: () => import('./auth/auth.module').then(m => m.NgxAuthModule)
|
||||
},
|
||||
{ path: '', redirectTo: 'auth', pathMatch: 'full' },
|
||||
{ path: '**', redirectTo: 'pages' },
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Akveo. All Rights Reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*/
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AngularFireModule } from '@angular/fire/compat';
|
||||
|
|
@ -22,6 +17,9 @@ import { CoreModule } from './@core/core.module';
|
|||
import { ThemeModule } from './@theme/theme.module';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { AngularFireAuthModule } from '@angular/fire/compat/auth';
|
||||
|
||||
import { AuthGuard } from './service/auth-guard.service';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
|
|
@ -31,6 +29,7 @@ import { AppComponent } from './app.component';
|
|||
HttpClientModule,
|
||||
AppRoutingModule,
|
||||
AngularFireModule.initializeApp(environment.firebase),
|
||||
AngularFireAuthModule,
|
||||
NbSidebarModule.forRoot(),
|
||||
NbMenuModule.forRoot(),
|
||||
NbDatepickerModule.forRoot(),
|
||||
|
|
@ -43,6 +42,9 @@ import { AppComponent } from './app.component';
|
|||
CoreModule.forRoot(),
|
||||
ThemeModule.forRoot(),
|
||||
],
|
||||
providers: [
|
||||
AuthGuard
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {
|
||||
|
|
|
|||
72
src/app/auth/auth-firebase.config.ts
Normal file
72
src/app/auth/auth-firebase.config.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { NbAuthStrategyOptions, NbAuthJWTToken, NbPasswordStrategyModule, NbPasswordStrategyMessage } from "@nebular/auth";
|
||||
|
||||
export class NbFirebasePasswordStrategyOptions extends NbAuthStrategyOptions {
|
||||
name: string = 'email';
|
||||
token = {
|
||||
class: NbAuthJWTToken,
|
||||
};
|
||||
register?: boolean | NbPasswordStrategyModule = {
|
||||
redirect: {
|
||||
success: '/auth/login',
|
||||
failure: null,
|
||||
},
|
||||
defaultErrors: ['Something went wrong, please try again.'],
|
||||
defaultMessages: ['You have been successfully registered.'],
|
||||
};
|
||||
login?: boolean | NbPasswordStrategyModule = {
|
||||
redirect: {
|
||||
success: '/dashboard',
|
||||
failure: null,
|
||||
},
|
||||
defaultErrors: ['Login/Email combination is not correct, please try again.'],
|
||||
defaultMessages: ['You have been successfully logged in.'],
|
||||
};
|
||||
logout?: boolean | NbPasswordStrategyModule = {
|
||||
redirect: {
|
||||
success: '/',
|
||||
failure: null,
|
||||
},
|
||||
defaultErrors: ['Something went wrong, please try again.'],
|
||||
defaultMessages: ['You have been successfully logged out.'],
|
||||
};
|
||||
refreshToken?: boolean | NbPasswordStrategyModule = {
|
||||
redirect: {
|
||||
success: null,
|
||||
failure: null,
|
||||
},
|
||||
defaultErrors: ['Something went wrong, please try again.'],
|
||||
defaultMessages: ['Your token has been successfully refreshed.'],
|
||||
};
|
||||
// requestPassword?: boolean | NbPasswordStrategyModule = {
|
||||
// redirect: {
|
||||
// success: '/',
|
||||
// failure: null,
|
||||
// },
|
||||
// defaultErrors: ['Something went wrong, please try again.'],
|
||||
// defaultMessages: ['Reset password instructions have been sent to your email.'],
|
||||
// };
|
||||
// resetPassword?: boolean | NbPasswordStrategyModule = {
|
||||
// redirect: {
|
||||
// success: '/',
|
||||
// failure: null,
|
||||
// },
|
||||
// defaultErrors: ['Something went wrong, please try again.'],
|
||||
// defaultMessages: ['Your password has been successfully changed.'],
|
||||
// };
|
||||
// errors?: NbPasswordStrategyMessage = {
|
||||
// key: 'message',
|
||||
// getter: (module: string, res, options: NbFirebasePasswordStrategyOptions) => getDeepFromObject(
|
||||
// res,
|
||||
// options.errors.key,
|
||||
// options[module].defaultErrors,
|
||||
// ),
|
||||
// };
|
||||
// messages?: NbPasswordStrategyMessage = {
|
||||
// key: 'messages',
|
||||
// getter: (module: string, res, options: NbFirebasePasswordStrategyOptions) => getDeepFromObject(
|
||||
// res.body,
|
||||
// options.messages.key,
|
||||
// options[module].defaultMessages,
|
||||
// ),
|
||||
// };
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms';
|
|||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { NgxAuthRoutingModule } from './auth-routing.module';
|
||||
import { NbAuthModule } from '@nebular/auth';
|
||||
import { NbAuthModule, NbPasswordAuthStrategy } from '@nebular/auth';
|
||||
import {
|
||||
NbAlertModule,
|
||||
NbButtonModule,
|
||||
|
|
@ -13,6 +13,7 @@ import {
|
|||
} from '@nebular/theme';
|
||||
import { NgxLoginComponent } from './login/login.component';
|
||||
import { NgxRegisterComponent } from './register/register.component';
|
||||
import { NbFirebasePasswordStrategyOptions } from './auth-firebase.config';
|
||||
|
||||
|
||||
@NgModule({
|
||||
|
|
@ -26,7 +27,29 @@ import { NgxRegisterComponent } from './register/register.component';
|
|||
NbCheckboxModule,
|
||||
NgxAuthRoutingModule,
|
||||
|
||||
NbAuthModule,
|
||||
NbAuthModule.forRoot({
|
||||
strategies: [
|
||||
NbPasswordAuthStrategy.setup({
|
||||
name: 'email',
|
||||
|
||||
login: {
|
||||
redirect: {
|
||||
success: '/dashboard',
|
||||
failure: null, // stay on the same page
|
||||
},
|
||||
},
|
||||
|
||||
register: {
|
||||
redirect: {
|
||||
success: '/auth/login',
|
||||
failure: null, // stay on the same page
|
||||
},
|
||||
},
|
||||
...new NbFirebasePasswordStrategyOptions()
|
||||
}),
|
||||
],
|
||||
forms: {},
|
||||
}),
|
||||
],
|
||||
declarations: [
|
||||
// ... here goes our new components
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<h1 id="title" class="title">Login</h1>
|
||||
<p class="sub-title">Welcome to RESUME TAILOR!!!</p>
|
||||
|
||||
<nb-alert
|
||||
<!-- <nb-alert
|
||||
*ngIf="showMessages.error && errors?.length && !submitted"
|
||||
outline="danger"
|
||||
role="alert"
|
||||
|
|
@ -10,9 +10,9 @@
|
|||
<ul class="alert-message-list">
|
||||
<li *ngFor="let error of errors" class="alert-message">{{ error }}</li>
|
||||
</ul>
|
||||
</nb-alert>
|
||||
</nb-alert> -->
|
||||
|
||||
<nb-alert
|
||||
<!-- <nb-alert
|
||||
*ngIf="showMessages.success && messages?.length && !submitted"
|
||||
outline="success"
|
||||
role="alert"
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
{{ message }}
|
||||
</li>
|
||||
</ul>
|
||||
</nb-alert>
|
||||
</nb-alert> -->
|
||||
|
||||
<form (ngSubmit)="login()" #form="ngForm" aria-labelledby="title">
|
||||
<div class="form-control-group">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component, ChangeDetectorRef } from '@angular/core';
|
||||
import { AngularFireAuth } from '@angular/fire/compat/auth';
|
||||
import { Router } from '@angular/router'; // Import Router from @angular/router
|
||||
import { NbAuthService } from '@nebular/auth';
|
||||
import { NbLoginComponent } from '@nebular/auth';
|
||||
|
||||
@Component({
|
||||
|
|
@ -6,4 +9,63 @@ import { NbLoginComponent } from '@nebular/auth';
|
|||
templateUrl: './login.component.html',
|
||||
})
|
||||
export class NgxLoginComponent extends NbLoginComponent {
|
||||
constructor(
|
||||
private fireAuth: AngularFireAuth,
|
||||
private authService: NbAuthService,
|
||||
public cd: ChangeDetectorRef, // Change to public or protected if NbLoginComponent's cd is public or protected
|
||||
public router: Router // Import Router from @angular/router
|
||||
) {
|
||||
super(authService, {}, cd, router);
|
||||
}
|
||||
|
||||
async login() {
|
||||
try {
|
||||
await this.fireAuth.signInWithEmailAndPassword(this.user.email, this.user.password);
|
||||
const user = await this.fireAuth.currentUser; // Get current user
|
||||
if (user) {
|
||||
const accessToken = await user.getIdToken(); // Get Firebase Authentication token (access token)
|
||||
const expiresIn = new Date().getTime() + 3600 * 1000; // Token expiration time (1 hour)
|
||||
localStorage.setItem('accessToken', accessToken);
|
||||
localStorage.setItem('accessTokenExpiresIn', expiresIn.toString()); // Store token expiration time
|
||||
this.router.navigate(['dashboard']); // Redirect to dashboard
|
||||
this.scheduleTokenRefresh(); // Schedule token refresh
|
||||
}
|
||||
console.log("Successfully Logged in.")
|
||||
} catch (error) {
|
||||
// Handle login errors here
|
||||
console.error('Error signing in:', error);
|
||||
// You can also display error messages to the user using this.errors array
|
||||
this.errors = ['Error signing in. Please try again.'];
|
||||
}
|
||||
}
|
||||
|
||||
private async scheduleTokenRefresh(): Promise<void> {
|
||||
const expiresInString = localStorage.getItem('accessTokenExpiresIn');
|
||||
if (expiresInString) {
|
||||
const expiresIn = +expiresInString;
|
||||
const timeUntilRefresh = expiresIn - new Date().getTime() - 300000; // Refresh token 5 minutes before expiration
|
||||
if (timeUntilRefresh > 0) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const user = await this.fireAuth.currentUser;
|
||||
if (user) {
|
||||
const newAccessToken = await user.getIdToken();
|
||||
localStorage.setItem('accessToken', newAccessToken);
|
||||
const newExpiresIn = new Date().getTime() + 3600 * 1000; // Extend expiration time by 1 hour
|
||||
localStorage.setItem('accessTokenExpiresIn', newExpiresIn.toString());
|
||||
this.scheduleTokenRefresh();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Token refresh failed:', error);
|
||||
}
|
||||
}, timeUntilRefresh);
|
||||
}
|
||||
} else {
|
||||
console.error('Access token expiration time is not available.');
|
||||
}
|
||||
}
|
||||
|
||||
getConfigValue(key: string): any {
|
||||
// Implement this method based on your configuration retrieval logic
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<h1 id="title" class="title">Register</h1>
|
||||
|
||||
<nb-alert
|
||||
<!-- <nb-alert
|
||||
*ngIf="showMessages.error && errors?.length && !submitted"
|
||||
outline="danger"
|
||||
role="alert"
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
{{ message }}
|
||||
</li>
|
||||
</ul>
|
||||
</nb-alert>
|
||||
</nb-alert> -->
|
||||
|
||||
<form (ngSubmit)="register()" #form="ngForm" aria-labelledby="title">
|
||||
<div class="form-control-group">
|
||||
|
|
|
|||
|
|
@ -1,9 +1,42 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { NbRegisterComponent } from '@nebular/auth';
|
||||
import { AngularFireAuth } from '@angular/fire/compat/auth';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-register',
|
||||
templateUrl: './register.component.html',
|
||||
})
|
||||
export class NgxRegisterComponent extends NbRegisterComponent {
|
||||
export class NgxRegisterComponent {
|
||||
user = {
|
||||
fullName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: ''
|
||||
};
|
||||
submitted = false;
|
||||
errors: string[] = [];
|
||||
|
||||
constructor(private fireAuth: AngularFireAuth, private router: Router) {}
|
||||
|
||||
async register() {
|
||||
this.submitted = true;
|
||||
try {
|
||||
if (this.user.password !== this.user.confirmPassword) {
|
||||
throw new Error('Password does not match the confirm password.');
|
||||
}
|
||||
|
||||
await this.fireAuth.createUserWithEmailAndPassword(this.user.email, this.user.password);
|
||||
// Registration successful, redirect to login page or dashboard
|
||||
this.router.navigate(['auth/login']);
|
||||
} catch (error) {
|
||||
// Handle registration errors
|
||||
console.error('Error registering user:', error);
|
||||
this.errors.push(error.message || 'An error occurred. Please try again later.');
|
||||
}
|
||||
this.submitted = false;
|
||||
}
|
||||
|
||||
getConfigValue(key: string): any {
|
||||
// Implement this method based on your configuration retrieval logic
|
||||
}
|
||||
}
|
||||
29
src/app/service/auth-guard.service.ts
Normal file
29
src/app/service/auth-guard.service.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, Router } from '@angular/router';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthGuard implements CanActivate {
|
||||
|
||||
constructor(private router: Router) {}
|
||||
|
||||
canActivate(): boolean {
|
||||
const accessToken = localStorage.getItem('accessToken');
|
||||
const accessTokenExpiresIn = localStorage.getItem('accessTokenExpiresIn');
|
||||
|
||||
// Check if access token and expiration time exist
|
||||
if (accessToken && accessTokenExpiresIn) {
|
||||
// Check if the access token is not expired
|
||||
const currentTime = new Date().getTime();
|
||||
const expiresIn = +accessTokenExpiresIn;
|
||||
if (currentTime < expiresIn) {
|
||||
return true; // Allow access to the route
|
||||
}
|
||||
}
|
||||
|
||||
// If access token is not found or expired, navigate to login page
|
||||
this.router.navigate(['/auth/login']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
30
src/app/service/auth.service.ts
Normal file
30
src/app/service/auth.service.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { AngularFireAuth } from '@angular/fire/compat/auth';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthService {
|
||||
|
||||
user$: Observable<firebase.default.User | null>;
|
||||
|
||||
constructor(private fireAuth: AngularFireAuth, private router: Router) {
|
||||
this.user$ = this.fireAuth.authState;
|
||||
}
|
||||
|
||||
// logout method
|
||||
async logout(): Promise<void> {
|
||||
try {
|
||||
await this.fireAuth.signOut();
|
||||
localStorage.removeItem('accessToken');
|
||||
localStorage.removeItem('accessTokenExpiresIn');
|
||||
this.router.navigate(['dashboard'], { queryParams: { logout: true } });
|
||||
} catch (error: any) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/app/utils/response-wrapper.ts
Normal file
6
src/app/utils/response-wrapper.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export interface ResponseWrapper<T> {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data: T;
|
||||
error?: any;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue