[Feature Request] authCanMatchGuard + helpers
- 主要语言
- TypeScript
- 星标
- 7.8k
- 派生
- 2.2k
- 平均合并
- 22 小时 28 分钟
- 30 天内合并 PR
- 6
描述
# 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))
```
贡献指南
调研方向
先阅读 src/auth-guard/auth-guard.ts 和 site/src/auth/route-guards.md,然后将它们与提议的 auth-can-match.guard.ts 实现进行比较。完成的标准是将功能性的 canMatch guard 和 helper 集成到 AngularFire 中,并为它们的身份验证和重定向行为添加测试。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- angular, firebase, typescript
- 领域
- authentication, frontend
- Issue 类型
- 功能
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 停滞
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100