angular / angular/angularfire

[Feature Request] authCanMatchGuard + helpers

Aperta
#3,544 1 commento 3 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
TypeScript
Stelle
7.8k
Fork
2.2k
Merge medio
22h 28m
PR unite (30g)
6

Descrizione

# 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))
```

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Leggi prima src/auth-guard/auth-guard.ts e site/src/auth/route-guards.md, quindi confrontali con l'implementazione proposta di auth-can-match.guard.ts. Il lavoro è completato quando il guard canMatch funzionale e gli helper sono integrati in AngularFire e vengono aggiunti test per il loro comportamento di autenticazione e reindirizzamento.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
angular, firebase, typescript
Ambito
authentication, frontend
Tipo di issue
Funzionalità
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.