angular / angular/angularfire

[Feature Request] authCanMatchGuard + helpers

Abierto
#3,544 1 comentario 3 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
TypeScript
Estrellas
7.8k
Forks
2.2k
Merge medio
22 h 28 min
PR fusionados (30 d)
6

Descripción

# Feature Request - authCanMatchGuard + helpers
## Description
The current auth-guard provided with angular fire is still using the [now deprecated](https://github.com/angular/angular/pull/47924) class based approach and targets the `canActivate` property on route definitions.

To freshen things up a bit, I have generated a new guard which utilizes the new functional approach and targets the newer `canMatch` route definition property. This has the added benefit of preventing components from being unnecessarily loaded which seems to work well and should be more performant.

I based the new code off of the previous implementation [documented here](https://github.com/angular/angularfire/blob/3639e41b52c0b2963c1e24734ff8401f4d21107e/site/src/auth/route-guards.md). You will notice similarities with [the previous guard code here](https://github.com/angular/angularfire/blob/3639e41b52c0b2963c1e24734ff8401f4d21107e/src/auth-guard/auth-guard.ts). Would love to see this incorporated into the AngularFire package but honestly I'm too lazy to write tests 😅 so if someone would be interested in owning that and collaborating, I'd be happy to open up a PR.

## Code
`auth-can-match.guard.ts`
```
import { inject } from '@angular/core';
import { Auth, user, User } from '@angular/fire/auth';
import { CanMatchFn, Route, Router, UrlSegment } from '@angular/router';
import { Observable, of, pipe, UnaryFunction } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';

/** Utility type to allow for adding an additional property to the CanMatchFn type */
type ExtendFn = BaseFnT extends (...a: infer P) => infer R
? (...a: [...P, ...AddPT]) => R
: never

export type AuthPipe = UnaryFunction, Observable>;
export const authCanMatchGuard: ExtendFn = (route, segments, authPipe?) => {
const auth = inject(Auth)
const user$ = user(auth)
const router = inject(Router)

const authPipeFactory = authPipe
? authPipe
: loggedIn

return user$
.pipe(
take(1),
authPipeFactory,
map(can => {
if (typeof can === 'boolean') {
return can;
} else if (Array.isArray(can)) {
return router.createUrlTree(can)
} else {
return router.parseUrl(can)
}
})
)
}

/** canMatch helper to be used with spread operator to make route definitions more concise.
* @example
* const redirectUnauthorizedToLogin = redirectUnauthorizedTo(['login'])
* ...
* export const routes: Routes = [
* {
* ...authCanMatch(redirectUnauthorizedToLogin)
* path: 'authProtectedRoute'
* },
*/
export const authCanMatch = (authPipe: AuthPipe) =>
({ canMatch: [(route: Route, segments: UrlSegment[]) => authCanMatchGuard(route, segments, authPipe)] })

/** Predefined auth guard helper pipes. So friendly & helpful 😍 */
export const loggedIn: AuthPipe = map(user => !!user)
export const isNotAnonymous: AuthPipe = map(user => !!user && !user.isAnonymous)
export const idTokenResult = switchMap((user: User | null) => user ? user.getIdTokenResult() : of(null))
export const emailVerified: AuthPipe = map(user => !!user && user.emailVerified)
export const customClaims = pipe(idTokenResult, map(idTokenResult => idTokenResult ? idTokenResult.claims : []))
export const hasCustomClaim: (claim: string) => AuthPipe =
// eslint-disable-next-line no-prototype-builtins
(claim) => pipe(customClaims, map(claims => claims.hasOwnProperty(claim)))
export const redirectUnauthorizedTo: (redirect: string | any[]) => AuthPipe =
(redirect) => pipe(loggedIn, map(loggedIn => loggedIn || redirect))
export const redirectLoggedInTo: (redirect: string | any[]) => AuthPipe =
(redirect) => pipe(loggedIn, map(loggedIn => loggedIn && redirect || true))
```

Guía de contribución

Abrir la guía de contribución

Línea de trabajo

Lee primero src/auth-guard/auth-guard.ts y site/src/auth/route-guards.md; después, compáralos con la implementación propuesta de auth-can-match.guard.ts. La tarea estará terminada cuando el guard canMatch funcional y sus helpers estén integrados en AngularFire y se hayan añadido pruebas para su comportamiento de autenticación y redirección.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
angular, firebase, typescript
Área
authentication, frontend
Tipo de issue
Nueva funcionalidad
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.