@mintplayer/ng-spark-auth 22.0.1 → 22.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs +5 -3
  2. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs.map +1 -1
  3. package/fesm2022/mintplayer-ng-spark-auth-core.mjs +73 -2
  4. package/fesm2022/mintplayer-ng-spark-auth-core.mjs.map +1 -1
  5. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs +1 -1
  6. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs.map +1 -1
  7. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs +2 -2
  8. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs.map +1 -1
  9. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs +2 -2
  10. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs.map +1 -1
  11. package/fesm2022/mintplayer-ng-spark-auth-login.mjs +1 -1
  12. package/fesm2022/mintplayer-ng-spark-auth-login.mjs.map +1 -1
  13. package/fesm2022/mintplayer-ng-spark-auth-models.mjs +41 -2
  14. package/fesm2022/mintplayer-ng-spark-auth-models.mjs.map +1 -1
  15. package/fesm2022/mintplayer-ng-spark-auth-register.mjs +1 -1
  16. package/fesm2022/mintplayer-ng-spark-auth-register.mjs.map +1 -1
  17. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs +1 -1
  18. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs.map +1 -1
  19. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs +48 -30
  20. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs.map +1 -1
  21. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs +80 -0
  22. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs.map +1 -0
  23. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs +1 -1
  24. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs.map +1 -1
  25. package/package.json +5 -1
  26. package/types/mintplayer-ng-spark-auth-auth-bar.d.ts +2 -0
  27. package/types/mintplayer-ng-spark-auth-core.d.ts +23 -1
  28. package/types/mintplayer-ng-spark-auth-forgot-password.d.ts +1 -1
  29. package/types/mintplayer-ng-spark-auth-login.d.ts +1 -1
  30. package/types/mintplayer-ng-spark-auth-models.d.ts +91 -5
  31. package/types/mintplayer-ng-spark-auth-register.d.ts +1 -1
  32. package/types/mintplayer-ng-spark-auth-reset-password.d.ts +1 -1
  33. package/types/mintplayer-ng-spark-auth-routes.d.ts +15 -0
  34. package/types/mintplayer-ng-spark-auth-sign-in.d.ts +36 -0
  35. package/types/mintplayer-ng-spark-auth-two-factor.d.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-auth-register.mjs","sources":["../../register/src/spark-register.component.ts","../../register/src/spark-register.component.html","../../register/mintplayer-ng-spark-auth-register.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { ReactiveFormsModule, FormBuilder, Validators, AbstractControl, ValidationErrors } from '@angular/forms';\nimport { RouterLink, Router } from '@angular/router';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SPARK_AUTH_ROUTE_PATHS } from '@mintplayer/ng-spark-auth/models';\nimport { SparkAuthService, SparkAuthTranslationService } from '@mintplayer/ng-spark-auth/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark-auth/pipes';\n\nfunction passwordMatchValidator(control: AbstractControl): ValidationErrors | null {\n const password = control.get('password');\n const confirmPassword = control.get('confirmPassword');\n if (password && confirmPassword && password.value !== confirmPassword.value) {\n return { passwordMismatch: true };\n }\n return null;\n}\n\n@Component({\n selector: 'spark-register',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe],\n templateUrl: './spark-register.component.html',\n})\nexport class SparkRegisterComponent {\n private readonly fb = inject(FormBuilder);\n private readonly authService = inject(SparkAuthService);\n private readonly router = inject(Router);\n private readonly translation = inject(SparkAuthTranslationService);\n readonly routePaths = inject(SPARK_AUTH_ROUTE_PATHS);\n\n colors = Color;\n readonly loading = signal(false);\n readonly errorMessage = signal('');\n\n readonly form = this.fb.group({\n email: ['', [Validators.required, Validators.email]],\n password: ['', Validators.required],\n confirmPassword: ['', Validators.required],\n }, { validators: passwordMatchValidator });\n\n async onSubmit(): Promise<void> {\n if (this.form.invalid) {\n this.form.markAllAsTouched();\n return;\n }\n\n this.loading.set(true);\n this.errorMessage.set('');\n\n const { email, password } = this.form.value;\n\n try {\n await this.authService.register(email!, password!);\n this.router.navigate([this.routePaths.login], {\n queryParams: { registered: 'true' },\n });\n } catch (err: any) {\n if (err instanceof HttpErrorResponse) {\n if (err.status === 400 && err.error?.errors) {\n const messages = ([] as string[]).concat(...Object.values(err.error.errors) as string[][]);\n this.errorMessage.set(messages.join(' '));\n } else if (err.error?.detail) {\n this.errorMessage.set(err.error.detail);\n } else {\n this.errorMessage.set(this.translation.t('auth.registrationFailed'));\n }\n } else {\n this.errorMessage.set(this.translation.t('auth.registrationFailed'));\n }\n } finally {\n this.loading.set(false);\n }\n }\n}\n\nexport default SparkRegisterComponent;\n","<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.register' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'auth.email' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n @if (form.get('email')?.touched && form.get('email')?.hasError('email')) {\n <div class=\"text-danger mt-1\">{{ 'auth.invalidEmail' | t }}</div>\n }\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'auth.password' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'auth.confirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'auth.passwordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.register' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <span>{{ 'auth.alreadyHaveAccount' | t }} </span>\n <a [routerLink]=\"routePaths.login\">{{ 'auth.login' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAaA,SAAS,sBAAsB,CAAC,OAAwB,EAAA;IACtD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACxC,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;AACtD,IAAA,IAAI,QAAQ,IAAI,eAAe,IAAI,QAAQ,CAAC,KAAK,KAAK,eAAe,CAAC,KAAK,EAAE;AAC3E,QAAA,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE;IACnC;AACA,IAAA,OAAO,IAAI;AACb;MASa,sBAAsB,CAAA;AAChB,IAAA,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;AACxB,IAAA,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC;AACtC,IAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AACvB,IAAA,WAAW,GAAG,MAAM,CAAC,2BAA2B,CAAC;AACzD,IAAA,UAAU,GAAG,MAAM,CAAC,sBAAsB,CAAC;IAEpD,MAAM,GAAG,KAAK;IACL,OAAO,GAAG,MAAM,CAAC,KAAK;gFAAC;IACvB,YAAY,GAAG,MAAM,CAAC,EAAE;qFAAC;AAEzB,IAAA,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;AAC5B,QAAA,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;AACpD,QAAA,QAAQ,EAAE,CAAC,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC;AACnC,QAAA,eAAe,EAAE,CAAC,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC;AAC3C,KAAA,EAAE,EAAE,UAAU,EAAE,sBAAsB,EAAE,CAAC;AAE1C,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;AACrB,YAAA,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE;YAC5B;QACF;AAEA,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;QAEzB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK;AAE3C,QAAA,IAAI;YACF,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAM,EAAE,QAAS,CAAC;AAClD,YAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;AAC5C,gBAAA,WAAW,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;AACpC,aAAA,CAAC;QACJ;QAAE,OAAO,GAAQ,EAAE;AACjB,YAAA,IAAI,GAAG,YAAY,iBAAiB,EAAE;AACpC,gBAAA,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE;AAC3C,oBAAA,MAAM,QAAQ,GAAI,EAAe,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAe,CAAC;AAC1F,oBAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC3C;AAAO,qBAAA,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE;oBAC5B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;gBACzC;qBAAO;AACL,oBAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC;gBACtE;YACF;iBAAO;AACL,gBAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC;YACtE;QACF;gBAAU;AACR,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;QACzB;IACF;uGAjDW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAtB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,sBAAsB,0EC7BnC,k0EAoEA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED1CY,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,aAAA,EAAA,QAAA,EAAA,8CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,wSAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,sGAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,kBAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,CAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,UAAA,CAAA,EAAA,QAAA,EAAA,CAAA,QAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,MAAA,EAAA,CAAA,iBAAA,EAAA,UAAA,EAAA,SAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,UAAU,kPAAE,gBAAgB,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,iBAAA,EAAA,qBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,eAAe,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,qBAAqB,0FAAE,eAAe,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,QAAA,CAAA,EAAA,OAAA,EAAA,CAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,sBAAsB,EAAA,QAAA,EAAA,6EAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,kBAAkB,6EAAE,gBAAgB,EAAA,IAAA,EAAA,GAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAGvK,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBAPlC,SAAS;+BACE,gBAAgB,EAAA,UAAA,EACd,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC,CAAC,mBAAmB,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,eAAe,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,gBAAgB,CAAC,EAAA,QAAA,EAAA,k0EAAA,EAAA;;;AE1BrL;;AAEG;;;;"}
1
+ {"version":3,"file":"mintplayer-ng-spark-auth-register.mjs","sources":["../../register/src/spark-register.component.ts","../../register/src/spark-register.component.html","../../register/mintplayer-ng-spark-auth-register.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { ReactiveFormsModule, FormBuilder, Validators, AbstractControl, ValidationErrors } from '@angular/forms';\nimport { RouterLink, Router } from '@angular/router';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SPARK_AUTH_ROUTE_PATHS } from '@mintplayer/ng-spark-auth/models';\nimport { SparkAuthService, SparkAuthTranslationService } from '@mintplayer/ng-spark-auth/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark-auth/pipes';\n\nfunction passwordMatchValidator(control: AbstractControl): ValidationErrors | null {\n const password = control.get('password');\n const confirmPassword = control.get('confirmPassword');\n if (password && confirmPassword && password.value !== confirmPassword.value) {\n return { passwordMismatch: true };\n }\n return null;\n}\n\n@Component({\n selector: 'spark-register',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe],\n templateUrl: './spark-register.component.html',\n})\nexport class SparkRegisterComponent {\n private readonly fb = inject(FormBuilder);\n private readonly authService = inject(SparkAuthService);\n private readonly router = inject(Router);\n private readonly translation = inject(SparkAuthTranslationService);\n readonly routePaths = inject(SPARK_AUTH_ROUTE_PATHS);\n\n colors = Color;\n readonly loading = signal(false);\n readonly errorMessage = signal('');\n\n readonly form = this.fb.group({\n email: ['', [Validators.required, Validators.email]],\n password: ['', Validators.required],\n confirmPassword: ['', Validators.required],\n }, { validators: passwordMatchValidator });\n\n async onSubmit(): Promise<void> {\n if (this.form.invalid) {\n this.form.markAllAsTouched();\n return;\n }\n\n this.loading.set(true);\n this.errorMessage.set('');\n\n const { email, password } = this.form.value;\n\n try {\n await this.authService.register(email!, password!);\n this.router.navigate([this.routePaths.login], {\n queryParams: { registered: 'true' },\n });\n } catch (err: any) {\n if (err instanceof HttpErrorResponse) {\n if (err.status === 400 && err.error?.errors) {\n const messages = ([] as string[]).concat(...Object.values(err.error.errors) as string[][]);\n this.errorMessage.set(messages.join(' '));\n } else if (err.error?.detail) {\n this.errorMessage.set(err.error.detail);\n } else {\n this.errorMessage.set(this.translation.t('auth.registrationFailed'));\n }\n } else {\n this.errorMessage.set(this.translation.t('auth.registrationFailed'));\n }\n } finally {\n this.loading.set(false);\n }\n }\n}\n\nexport default SparkRegisterComponent;\n","<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.register' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'auth.email' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n @if (form.get('email')?.touched && form.get('email')?.hasError('email')) {\n <div class=\"text-danger mt-1\">{{ 'auth.invalidEmail' | t }}</div>\n }\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'auth.password' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'auth.confirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'auth.passwordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.register' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <span>{{ 'auth.alreadyHaveAccount' | t }} </span>\n <a [routerLink]=\"routePaths.login\">{{ 'auth.login' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -78,7 +78,7 @@ class SparkResetPasswordComponent {
78
78
  }
79
79
  }
80
80
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SparkResetPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
81
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: SparkResetPasswordComponent, isStandalone: true, selector: "spark-reset-password", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.resetPassword' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">\n {{ successMessage() }}\n <div class=\"mt-2\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.goToLogin' | t }}</a>\n </div>\n </bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"newPassword\" class=\"form-label\">{{ 'auth.newPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n formControlName=\"newPassword\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'auth.confirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'auth.passwordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.resetPassword' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.backToLogin' | t }}</a>\n </div>\n }\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["color", "outline"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["color", "navStyle"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
81
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: SparkResetPasswordComponent, isStandalone: true, selector: "spark-reset-password", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.resetPassword' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">\n {{ successMessage() }}\n <div class=\"mt-2\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.goToLogin' | t }}</a>\n </div>\n </bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"newPassword\" class=\"form-label\">{{ 'auth.newPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n formControlName=\"newPassword\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'auth.confirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'auth.passwordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.resetPassword' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.backToLogin' | t }}</a>\n </div>\n }\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "announce", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["color", "outline"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["color", "navStyle"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
82
82
  }
83
83
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SparkResetPasswordComponent, decorators: [{
84
84
  type: Component,
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-auth-reset-password.mjs","sources":["../../reset-password/src/spark-reset-password.component.ts","../../reset-password/src/spark-reset-password.component.html","../../reset-password/mintplayer-ng-spark-auth-reset-password.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\nimport { ReactiveFormsModule, FormBuilder, Validators, AbstractControl, ValidationErrors } from '@angular/forms';\nimport { RouterLink, ActivatedRoute, Router } from '@angular/router';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SPARK_AUTH_ROUTE_PATHS } from '@mintplayer/ng-spark-auth/models';\nimport { SparkAuthService, SparkAuthTranslationService } from '@mintplayer/ng-spark-auth/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark-auth/pipes';\n\nfunction passwordMatchValidator(control: AbstractControl): ValidationErrors | null {\n const password = control.get('newPassword');\n const confirmPassword = control.get('confirmPassword');\n if (password && confirmPassword && password.value !== confirmPassword.value) {\n return { passwordMismatch: true };\n }\n return null;\n}\n\n@Component({\n selector: 'spark-reset-password',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe],\n templateUrl: './spark-reset-password.component.html',\n})\nexport class SparkResetPasswordComponent implements OnInit {\n private readonly fb = inject(FormBuilder);\n private readonly authService = inject(SparkAuthService);\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly translation = inject(SparkAuthTranslationService);\n readonly routePaths = inject(SPARK_AUTH_ROUTE_PATHS);\n\n colors = Color;\n readonly loading = signal(false);\n readonly errorMessage = signal('');\n readonly successMessage = signal('');\n\n private email = '';\n private code = '';\n\n readonly form = this.fb.group({\n newPassword: ['', Validators.required],\n confirmPassword: ['', Validators.required],\n }, { validators: passwordMatchValidator });\n\n ngOnInit(): void {\n this.email = this.route.snapshot.queryParamMap.get('email') ?? '';\n this.code = this.route.snapshot.queryParamMap.get('code') ?? '';\n\n if (!this.email || !this.code) {\n this.errorMessage.set(this.translation.t('auth.invalidResetLink'));\n }\n }\n\n async onSubmit(): Promise<void> {\n if (this.form.invalid) {\n this.form.markAllAsTouched();\n return;\n }\n\n if (!this.email || !this.code) {\n this.errorMessage.set(this.translation.t('auth.invalidResetLink'));\n return;\n }\n\n this.loading.set(true);\n this.errorMessage.set('');\n this.successMessage.set('');\n\n const { newPassword } = this.form.value;\n\n try {\n await this.authService.resetPassword(this.email, this.code, newPassword!);\n this.successMessage.set(this.translation.t('auth.resetSuccess'));\n } catch (err: any) {\n if (err instanceof HttpErrorResponse && err.error?.detail) {\n this.errorMessage.set(err.error.detail);\n } else {\n this.errorMessage.set(this.translation.t('auth.resetFailed'));\n }\n } finally {\n this.loading.set(false);\n }\n }\n}\n\nexport default SparkResetPasswordComponent;\n","<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.resetPassword' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">\n {{ successMessage() }}\n <div class=\"mt-2\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.goToLogin' | t }}</a>\n </div>\n </bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"newPassword\" class=\"form-label\">{{ 'auth.newPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n formControlName=\"newPassword\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'auth.confirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'auth.passwordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.resetPassword' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.backToLogin' | t }}</a>\n </div>\n }\n </div>\n </bs-card>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mintplayer-ng-spark-auth-reset-password.mjs","sources":["../../reset-password/src/spark-reset-password.component.ts","../../reset-password/src/spark-reset-password.component.html","../../reset-password/mintplayer-ng-spark-auth-reset-password.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';\nimport { ReactiveFormsModule, FormBuilder, Validators, AbstractControl, ValidationErrors } from '@angular/forms';\nimport { RouterLink, ActivatedRoute, Router } from '@angular/router';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SPARK_AUTH_ROUTE_PATHS } from '@mintplayer/ng-spark-auth/models';\nimport { SparkAuthService, SparkAuthTranslationService } from '@mintplayer/ng-spark-auth/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark-auth/pipes';\n\nfunction passwordMatchValidator(control: AbstractControl): ValidationErrors | null {\n const password = control.get('newPassword');\n const confirmPassword = control.get('confirmPassword');\n if (password && confirmPassword && password.value !== confirmPassword.value) {\n return { passwordMismatch: true };\n }\n return null;\n}\n\n@Component({\n selector: 'spark-reset-password',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe],\n templateUrl: './spark-reset-password.component.html',\n})\nexport class SparkResetPasswordComponent implements OnInit {\n private readonly fb = inject(FormBuilder);\n private readonly authService = inject(SparkAuthService);\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly translation = inject(SparkAuthTranslationService);\n readonly routePaths = inject(SPARK_AUTH_ROUTE_PATHS);\n\n colors = Color;\n readonly loading = signal(false);\n readonly errorMessage = signal('');\n readonly successMessage = signal('');\n\n private email = '';\n private code = '';\n\n readonly form = this.fb.group({\n newPassword: ['', Validators.required],\n confirmPassword: ['', Validators.required],\n }, { validators: passwordMatchValidator });\n\n ngOnInit(): void {\n this.email = this.route.snapshot.queryParamMap.get('email') ?? '';\n this.code = this.route.snapshot.queryParamMap.get('code') ?? '';\n\n if (!this.email || !this.code) {\n this.errorMessage.set(this.translation.t('auth.invalidResetLink'));\n }\n }\n\n async onSubmit(): Promise<void> {\n if (this.form.invalid) {\n this.form.markAllAsTouched();\n return;\n }\n\n if (!this.email || !this.code) {\n this.errorMessage.set(this.translation.t('auth.invalidResetLink'));\n return;\n }\n\n this.loading.set(true);\n this.errorMessage.set('');\n this.successMessage.set('');\n\n const { newPassword } = this.form.value;\n\n try {\n await this.authService.resetPassword(this.email, this.code, newPassword!);\n this.successMessage.set(this.translation.t('auth.resetSuccess'));\n } catch (err: any) {\n if (err instanceof HttpErrorResponse && err.error?.detail) {\n this.errorMessage.set(err.error.detail);\n } else {\n this.errorMessage.set(this.translation.t('auth.resetFailed'));\n }\n } finally {\n this.loading.set(false);\n }\n }\n}\n\nexport default SparkResetPasswordComponent;\n","<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.resetPassword' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">\n {{ successMessage() }}\n <div class=\"mt-2\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.goToLogin' | t }}</a>\n </div>\n </bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"newPassword\" class=\"form-label\">{{ 'auth.newPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n formControlName=\"newPassword\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'auth.confirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'auth.passwordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.resetPassword' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.backToLogin' | t }}</a>\n </div>\n }\n </div>\n </bs-card>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,45 +1,63 @@
1
1
  import { SPARK_AUTH_ROUTE_PATHS } from '@mintplayer/ng-spark-auth/models';
2
2
 
3
- function resolveEntry(entry, defaultPath, defaultLoader) {
4
- if (entry === undefined || typeof entry === 'string') {
5
- return {
6
- path: typeof entry === 'string' ? entry : defaultPath,
7
- loadComponent: defaultLoader,
8
- };
9
- }
10
- return {
11
- path: entry.path,
12
- loadComponent: entry.component
13
- ? () => Promise.resolve(entry.component)
14
- : defaultLoader,
15
- };
3
+ /** The routed path for an entry, independent of how its component is loaded. */
4
+ function entryPath(entry, defaultPath) {
5
+ if (entry === undefined)
6
+ return defaultPath;
7
+ return typeof entry === 'string' ? entry : entry.path;
8
+ }
9
+ /** An explicitly supplied component wins over the library's lazy import. */
10
+ function child(entry, path, defaultLoader) {
11
+ const component = typeof entry === 'object' && entry.component ? entry.component : undefined;
12
+ return { path, loadComponent: component ? () => Promise.resolve(component) : defaultLoader };
16
13
  }
14
+ /**
15
+ * The routes for Spark's authentication pages.
16
+ *
17
+ * `config.localCredentials` chooses how much of the email/password family to route, mirroring the
18
+ * server's `SparkLocalCredentials`. `GET /spark/auth/capabilities` reports what the server is
19
+ * actually running, so the two can be checked against each other.
20
+ *
21
+ * The `import()` expressions live *inside* the branches that need them. That placement is the point:
22
+ * a bundler decides whether to emit a lazy chunk from whether the `import()` call site is reachable,
23
+ * not from whether the route object referencing it survives — so filtering the children array after
24
+ * the fact would still ship every page. Excluded pages must have no reachable `import()` at all.
25
+ *
26
+ * `SPARK_AUTH_ROUTE_PATHS` is still provided in full. The excluded pages are excluded together, so
27
+ * nothing that survives can link to something that does not, and the token stays `Required`.
28
+ */
17
29
  function sparkAuthRoutes(config) {
18
- const login = resolveEntry(config?.login, 'login', () => import('@mintplayer/ng-spark-auth/login').then(m => m.SparkLoginComponent));
19
- const twoFactor = resolveEntry(config?.twoFactor, 'login/two-factor', () => import('@mintplayer/ng-spark-auth/two-factor').then(m => m.SparkTwoFactorComponent));
20
- const register = resolveEntry(config?.register, 'register', () => import('@mintplayer/ng-spark-auth/register').then(m => m.SparkRegisterComponent));
21
- const forgotPassword = resolveEntry(config?.forgotPassword, 'forgot-password', () => import('@mintplayer/ng-spark-auth/forgot-password').then(m => m.SparkForgotPasswordComponent));
22
- const resetPassword = resolveEntry(config?.resetPassword, 'reset-password', () => import('@mintplayer/ng-spark-auth/reset-password').then(m => m.SparkResetPasswordComponent));
30
+ const mode = config?.localCredentials ?? 'full';
23
31
  const paths = {
24
- login: '/' + login.path,
25
- twoFactor: '/' + twoFactor.path,
26
- register: '/' + register.path,
27
- forgotPassword: '/' + forgotPassword.path,
28
- resetPassword: '/' + resetPassword.path,
32
+ signIn: '/' + entryPath(config?.signIn, 'sign-in'),
33
+ login: '/' + entryPath(config?.login, 'login'),
34
+ twoFactor: '/' + entryPath(config?.twoFactor, 'login/two-factor'),
35
+ register: '/' + entryPath(config?.register, 'register'),
36
+ forgotPassword: '/' + entryPath(config?.forgotPassword, 'forgot-password'),
37
+ resetPassword: '/' + entryPath(config?.resetPassword, 'reset-password'),
29
38
  };
39
+ const children = [];
40
+ // Only when local credentials are limited. In 'full' mode the login page is already the landing
41
+ // page, and emitting a second one would change the routes of every app that never opted in.
42
+ if (mode !== 'full') {
43
+ children.push(child(config?.signIn, entryPath(config?.signIn, 'sign-in'), () => import('@mintplayer/ng-spark-auth/sign-in').then(m => m.SparkSignInComponent)));
44
+ }
45
+ if (mode !== 'disabled') {
46
+ children.push(child(config?.login, entryPath(config?.login, 'login'), () => import('@mintplayer/ng-spark-auth/login').then(m => m.SparkLoginComponent)),
47
+ // Reachable only from the login page's RequiresTwoFactor branch, and it posts to the same
48
+ // /login endpoint — so it belongs to password sign-in, not to authentication in general.
49
+ child(config?.twoFactor, entryPath(config?.twoFactor, 'login/two-factor'), () => import('@mintplayer/ng-spark-auth/two-factor').then(m => m.SparkTwoFactorComponent)), child(config?.forgotPassword, entryPath(config?.forgotPassword, 'forgot-password'), () => import('@mintplayer/ng-spark-auth/forgot-password').then(m => m.SparkForgotPasswordComponent)), child(config?.resetPassword, entryPath(config?.resetPassword, 'reset-password'), () => import('@mintplayer/ng-spark-auth/reset-password').then(m => m.SparkResetPasswordComponent)));
50
+ }
51
+ if (mode === 'full') {
52
+ children.push(child(config?.register, entryPath(config?.register, 'register'), () => import('@mintplayer/ng-spark-auth/register').then(m => m.SparkRegisterComponent)));
53
+ }
30
54
  return [
31
55
  {
32
56
  path: '',
33
57
  providers: [
34
58
  { provide: SPARK_AUTH_ROUTE_PATHS, useValue: paths },
35
59
  ],
36
- children: [
37
- { path: login.path, loadComponent: login.loadComponent },
38
- { path: twoFactor.path, loadComponent: twoFactor.loadComponent },
39
- { path: register.path, loadComponent: register.loadComponent },
40
- { path: forgotPassword.path, loadComponent: forgotPassword.loadComponent },
41
- { path: resetPassword.path, loadComponent: resetPassword.loadComponent },
42
- ],
60
+ children,
43
61
  },
44
62
  ];
45
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-auth-routes.mjs","sources":["../../routes/src/spark-auth-routes.ts","../../routes/mintplayer-ng-spark-auth-routes.ts"],"sourcesContent":["import { SPARK_AUTH_ROUTE_PATHS, SparkAuthRouteConfig, SparkAuthRouteEntry, SparkAuthRoutePaths } from '@mintplayer/ng-spark-auth/models';\n\ninterface ResolvedEntry {\n path: string;\n loadComponent: () => Promise<any>;\n}\n\nfunction resolveEntry(\n entry: SparkAuthRouteEntry | undefined,\n defaultPath: string,\n defaultLoader: () => Promise<any>,\n): ResolvedEntry {\n if (entry === undefined || typeof entry === 'string') {\n return {\n path: typeof entry === 'string' ? entry : defaultPath,\n loadComponent: defaultLoader,\n };\n }\n return {\n path: entry.path,\n loadComponent: entry.component\n ? () => Promise.resolve(entry.component!)\n : defaultLoader,\n };\n}\n\nexport function sparkAuthRoutes(config?: SparkAuthRouteConfig): any[] {\n const login = resolveEntry(config?.login, 'login',\n () => import('@mintplayer/ng-spark-auth/login').then(m => m.SparkLoginComponent));\n const twoFactor = resolveEntry(config?.twoFactor, 'login/two-factor',\n () => import('@mintplayer/ng-spark-auth/two-factor').then(m => m.SparkTwoFactorComponent));\n const register = resolveEntry(config?.register, 'register',\n () => import('@mintplayer/ng-spark-auth/register').then(m => m.SparkRegisterComponent));\n const forgotPassword = resolveEntry(config?.forgotPassword, 'forgot-password',\n () => import('@mintplayer/ng-spark-auth/forgot-password').then(m => m.SparkForgotPasswordComponent));\n const resetPassword = resolveEntry(config?.resetPassword, 'reset-password',\n () => import('@mintplayer/ng-spark-auth/reset-password').then(m => m.SparkResetPasswordComponent));\n\n const paths: SparkAuthRoutePaths = {\n login: '/' + login.path,\n twoFactor: '/' + twoFactor.path,\n register: '/' + register.path,\n forgotPassword: '/' + forgotPassword.path,\n resetPassword: '/' + resetPassword.path,\n };\n\n return [\n {\n path: '',\n providers: [\n { provide: SPARK_AUTH_ROUTE_PATHS, useValue: paths },\n ],\n children: [\n { path: login.path, loadComponent: login.loadComponent },\n { path: twoFactor.path, loadComponent: twoFactor.loadComponent },\n { path: register.path, loadComponent: register.loadComponent },\n { path: forgotPassword.path, loadComponent: forgotPassword.loadComponent },\n { path: resetPassword.path, loadComponent: resetPassword.loadComponent },\n ],\n },\n ];\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;AAOA,SAAS,YAAY,CACnB,KAAsC,EACtC,WAAmB,EACnB,aAAiC,EAAA;IAEjC,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;QACpD,OAAO;AACL,YAAA,IAAI,EAAE,OAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,GAAG,WAAW;AACrD,YAAA,aAAa,EAAE,aAAa;SAC7B;IACH;IACA,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,aAAa,EAAE,KAAK,CAAC;cACjB,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,SAAU;AACxC,cAAE,aAAa;KAClB;AACH;AAEM,SAAU,eAAe,CAAC,MAA6B,EAAA;AAC3D,IAAA,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAC/C,MAAM,OAAO,iCAAiC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,mBAAmB,CAAC,CAAC;AACnF,IAAA,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAClE,MAAM,OAAO,sCAAsC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,uBAAuB,CAAC,CAAC;AAC5F,IAAA,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EACxD,MAAM,OAAO,oCAAoC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,sBAAsB,CAAC,CAAC;AACzF,IAAA,MAAM,cAAc,GAAG,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,iBAAiB,EAC3E,MAAM,OAAO,2CAA2C,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,4BAA4B,CAAC,CAAC;AACtG,IAAA,MAAM,aAAa,GAAG,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,gBAAgB,EACxE,MAAM,OAAO,0CAA0C,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,2BAA2B,CAAC,CAAC;AAEpG,IAAA,MAAM,KAAK,GAAwB;AACjC,QAAA,KAAK,EAAE,GAAG,GAAG,KAAK,CAAC,IAAI;AACvB,QAAA,SAAS,EAAE,GAAG,GAAG,SAAS,CAAC,IAAI;AAC/B,QAAA,QAAQ,EAAE,GAAG,GAAG,QAAQ,CAAC,IAAI;AAC7B,QAAA,cAAc,EAAE,GAAG,GAAG,cAAc,CAAC,IAAI;AACzC,QAAA,aAAa,EAAE,GAAG,GAAG,aAAa,CAAC,IAAI;KACxC;IAED,OAAO;AACL,QAAA;AACE,YAAA,IAAI,EAAE,EAAE;AACR,YAAA,SAAS,EAAE;AACT,gBAAA,EAAE,OAAO,EAAE,sBAAsB,EAAE,QAAQ,EAAE,KAAK,EAAE;AACrD,aAAA;AACD,YAAA,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE;gBACxD,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,SAAS,CAAC,aAAa,EAAE;gBAChE,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,CAAC,aAAa,EAAE;gBAC9D,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,aAAa,EAAE,cAAc,CAAC,aAAa,EAAE;gBAC1E,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,aAAa,CAAC,aAAa,EAAE;AACzE,aAAA;AACF,SAAA;KACF;AACH;;AC7DA;;AAEG;;;;"}
1
+ {"version":3,"file":"mintplayer-ng-spark-auth-routes.mjs","sources":["../../routes/src/spark-auth-routes.ts","../../routes/mintplayer-ng-spark-auth-routes.ts"],"sourcesContent":["import { SPARK_AUTH_ROUTE_PATHS, SparkAuthRouteConfig, SparkAuthRouteEntry, SparkAuthRoutePaths } from '@mintplayer/ng-spark-auth/models';\n\ntype Loader = () => Promise<any>;\n\ninterface Child {\n path: string;\n loadComponent: Loader;\n}\n\n/** The routed path for an entry, independent of how its component is loaded. */\nfunction entryPath(entry: SparkAuthRouteEntry | undefined, defaultPath: string): string {\n if (entry === undefined) return defaultPath;\n return typeof entry === 'string' ? entry : entry.path;\n}\n\n/** An explicitly supplied component wins over the library's lazy import. */\nfunction child(entry: SparkAuthRouteEntry | undefined, path: string, defaultLoader: Loader): Child {\n const component = typeof entry === 'object' && entry.component ? entry.component : undefined;\n return { path, loadComponent: component ? () => Promise.resolve(component) : defaultLoader };\n}\n\n/**\n * The routes for Spark's authentication pages.\n *\n * `config.localCredentials` chooses how much of the email/password family to route, mirroring the\n * server's `SparkLocalCredentials`. `GET /spark/auth/capabilities` reports what the server is\n * actually running, so the two can be checked against each other.\n *\n * The `import()` expressions live *inside* the branches that need them. That placement is the point:\n * a bundler decides whether to emit a lazy chunk from whether the `import()` call site is reachable,\n * not from whether the route object referencing it survives — so filtering the children array after\n * the fact would still ship every page. Excluded pages must have no reachable `import()` at all.\n *\n * `SPARK_AUTH_ROUTE_PATHS` is still provided in full. The excluded pages are excluded together, so\n * nothing that survives can link to something that does not, and the token stays `Required`.\n */\nexport function sparkAuthRoutes(config?: SparkAuthRouteConfig): any[] {\n const mode = config?.localCredentials ?? 'full';\n\n const paths: SparkAuthRoutePaths = {\n signIn: '/' + entryPath(config?.signIn, 'sign-in'),\n login: '/' + entryPath(config?.login, 'login'),\n twoFactor: '/' + entryPath(config?.twoFactor, 'login/two-factor'),\n register: '/' + entryPath(config?.register, 'register'),\n forgotPassword: '/' + entryPath(config?.forgotPassword, 'forgot-password'),\n resetPassword: '/' + entryPath(config?.resetPassword, 'reset-password'),\n };\n\n const children: Child[] = [];\n\n // Only when local credentials are limited. In 'full' mode the login page is already the landing\n // page, and emitting a second one would change the routes of every app that never opted in.\n if (mode !== 'full') {\n children.push(\n child(config?.signIn, entryPath(config?.signIn, 'sign-in'),\n () => import('@mintplayer/ng-spark-auth/sign-in').then(m => m.SparkSignInComponent)),\n );\n }\n\n if (mode !== 'disabled') {\n children.push(\n child(config?.login, entryPath(config?.login, 'login'),\n () => import('@mintplayer/ng-spark-auth/login').then(m => m.SparkLoginComponent)),\n // Reachable only from the login page's RequiresTwoFactor branch, and it posts to the same\n // /login endpoint — so it belongs to password sign-in, not to authentication in general.\n child(config?.twoFactor, entryPath(config?.twoFactor, 'login/two-factor'),\n () => import('@mintplayer/ng-spark-auth/two-factor').then(m => m.SparkTwoFactorComponent)),\n child(config?.forgotPassword, entryPath(config?.forgotPassword, 'forgot-password'),\n () => import('@mintplayer/ng-spark-auth/forgot-password').then(m => m.SparkForgotPasswordComponent)),\n child(config?.resetPassword, entryPath(config?.resetPassword, 'reset-password'),\n () => import('@mintplayer/ng-spark-auth/reset-password').then(m => m.SparkResetPasswordComponent)),\n );\n }\n\n if (mode === 'full') {\n children.push(\n child(config?.register, entryPath(config?.register, 'register'),\n () => import('@mintplayer/ng-spark-auth/register').then(m => m.SparkRegisterComponent)),\n );\n }\n\n return [\n {\n path: '',\n providers: [\n { provide: SPARK_AUTH_ROUTE_PATHS, useValue: paths },\n ],\n children,\n },\n ];\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, signal, isDevMode, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { RouterLink } from '@angular/router';
4
+ import { Color } from '@mintplayer/ng-bootstrap';
5
+ import { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';
6
+ import { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';
7
+ import { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';
8
+ import { SPARK_AUTH_ROUTE_PATHS } from '@mintplayer/ng-spark-auth/models';
9
+ import { SparkAuthService } from '@mintplayer/ng-spark-auth/core';
10
+ import { TranslateKeyPipe } from '@mintplayer/ng-spark-auth/pipes';
11
+
12
+ /**
13
+ * The sign-in landing page for an application whose local credentials are turned off — a button per
14
+ * external provider, and a link to the password form when there still is one.
15
+ *
16
+ * The providers come from `GET /spark/auth/capabilities` rather than from a list the application
17
+ * hard-codes. That is the point of the component: every consumer that hand-rolled this before wrote
18
+ * the scheme name as a string literal, which is a silent mismatch waiting to happen the moment the
19
+ * server's provider registration changes.
20
+ */
21
+ class SparkSignInComponent {
22
+ authService = inject(SparkAuthService);
23
+ routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
24
+ colors = Color;
25
+ providers = signal([], /* @ts-ignore */
26
+ ...(ngDevMode ? [{ debugName: "providers" }] : /* istanbul ignore next */ []));
27
+ localCredentialsAvailable = signal(false, /* @ts-ignore */
28
+ ...(ngDevMode ? [{ debugName: "localCredentialsAvailable" }] : /* istanbul ignore next */ []));
29
+ loading = signal(true, /* @ts-ignore */
30
+ ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
31
+ failed = signal(false, /* @ts-ignore */
32
+ ...(ngDevMode ? [{ debugName: "failed" }] : /* istanbul ignore next */ []));
33
+ constructor() {
34
+ void this.load();
35
+ }
36
+ async load() {
37
+ try {
38
+ const capabilities = await this.authService.capabilities();
39
+ this.providers.set(capabilities.externalProviders);
40
+ this.localCredentialsAvailable.set(capabilities.localCredentials !== 'Disabled');
41
+ this.warnOnMismatch(capabilities);
42
+ }
43
+ catch {
44
+ // A sign-in page that renders nothing looks identical to one whose providers all failed to
45
+ // load, so say which happened rather than showing an empty page.
46
+ this.failed.set(true);
47
+ }
48
+ finally {
49
+ this.loading.set(false);
50
+ }
51
+ }
52
+ /**
53
+ * The routes are a build-time decision and the server's mode a deployment-time one, so they can
54
+ * disagree without anything failing loudly. Reaching this page at all means the app routed it,
55
+ * which it only does when local credentials are meant to be limited.
56
+ */
57
+ warnOnMismatch(capabilities) {
58
+ if (isDevMode() && capabilities.localCredentials === 'Full') {
59
+ console.warn('[ng-spark-auth] This app routes the sign-in landing page, but the server reports '
60
+ + 'localCredentials = Full. The two are configured independently — check that '
61
+ + "sparkAuthRoutes({ localCredentials: … }) matches AddAuthentication's LocalCredentials mode.");
62
+ }
63
+ }
64
+ signInWith(provider) {
65
+ void this.authService.loginWithProvider(provider.scheme);
66
+ }
67
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SparkSignInComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
68
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: SparkSignInComponent, isStandalone: true, selector: "spark-sign-in", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.login' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (loading()) {\n <div class=\"text-center\"><bs-spinner /></div>\n } @else if (failed()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">{{ 'auth.signInUnavailable' | t }}</bs-alert>\n } @else {\n @for (provider of providers(); track provider.scheme) {\n <button type=\"button\" class=\"btn btn-outline-primary w-100 mb-2\" (click)=\"signInWith(provider)\">\n {{ provider.displayName }}\n </button>\n } @empty {\n <bs-alert [type]=\"colors.warning\" class=\"mb-0\">{{ 'auth.noSignInMethods' | t }}</bs-alert>\n }\n\n @if (localCredentialsAvailable()) {\n <div class=\"text-center mt-3\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.login' | t }}</a>\n </div>\n }\n }\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "announce", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["color", "outline"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["color", "navStyle"] }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
69
+ }
70
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SparkSignInComponent, decorators: [{
71
+ type: Component,
72
+ args: [{ selector: 'spark-sign-in', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.login' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (loading()) {\n <div class=\"text-center\"><bs-spinner /></div>\n } @else if (failed()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">{{ 'auth.signInUnavailable' | t }}</bs-alert>\n } @else {\n @for (provider of providers(); track provider.scheme) {\n <button type=\"button\" class=\"btn btn-outline-primary w-100 mb-2\" (click)=\"signInWith(provider)\">\n {{ provider.displayName }}\n </button>\n } @empty {\n <bs-alert [type]=\"colors.warning\" class=\"mb-0\">{{ 'auth.noSignInMethods' | t }}</bs-alert>\n }\n\n @if (localCredentialsAvailable()) {\n <div class=\"text-center mt-3\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.login' | t }}</a>\n </div>\n }\n }\n </div>\n </bs-card>\n</div>\n" }]
73
+ }], ctorParameters: () => [] });
74
+
75
+ /**
76
+ * Generated bundle index. Do not edit.
77
+ */
78
+
79
+ export { SparkSignInComponent };
80
+ //# sourceMappingURL=mintplayer-ng-spark-auth-sign-in.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mintplayer-ng-spark-auth-sign-in.mjs","sources":["../../sign-in/src/spark-sign-in.component.ts","../../sign-in/src/spark-sign-in.component.html","../../sign-in/mintplayer-ng-spark-auth-sign-in.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, isDevMode, signal } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SPARK_AUTH_ROUTE_PATHS, SparkAuthCapabilities, SparkExternalProvider } from '@mintplayer/ng-spark-auth/models';\nimport { SparkAuthService } from '@mintplayer/ng-spark-auth/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark-auth/pipes';\n\n/**\n * The sign-in landing page for an application whose local credentials are turned off — a button per\n * external provider, and a link to the password form when there still is one.\n *\n * The providers come from `GET /spark/auth/capabilities` rather than from a list the application\n * hard-codes. That is the point of the component: every consumer that hand-rolled this before wrote\n * the scheme name as a string literal, which is a silent mismatch waiting to happen the moment the\n * server's provider registration changes.\n */\n@Component({\n selector: 'spark-sign-in',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsSpinnerComponent, TranslateKeyPipe],\n templateUrl: './spark-sign-in.component.html',\n})\nexport class SparkSignInComponent {\n private readonly authService = inject(SparkAuthService);\n readonly routePaths = inject(SPARK_AUTH_ROUTE_PATHS);\n readonly colors = Color;\n\n readonly providers = signal<SparkExternalProvider[]>([]);\n readonly localCredentialsAvailable = signal(false);\n readonly loading = signal(true);\n readonly failed = signal(false);\n\n constructor() {\n void this.load();\n }\n\n private async load(): Promise<void> {\n try {\n const capabilities = await this.authService.capabilities();\n this.providers.set(capabilities.externalProviders);\n this.localCredentialsAvailable.set(capabilities.localCredentials !== 'Disabled');\n this.warnOnMismatch(capabilities);\n } catch {\n // A sign-in page that renders nothing looks identical to one whose providers all failed to\n // load, so say which happened rather than showing an empty page.\n this.failed.set(true);\n } finally {\n this.loading.set(false);\n }\n }\n\n /**\n * The routes are a build-time decision and the server's mode a deployment-time one, so they can\n * disagree without anything failing loudly. Reaching this page at all means the app routed it,\n * which it only does when local credentials are meant to be limited.\n */\n private warnOnMismatch(capabilities: SparkAuthCapabilities): void {\n if (isDevMode() && capabilities.localCredentials === 'Full') {\n console.warn(\n '[ng-spark-auth] This app routes the sign-in landing page, but the server reports '\n + 'localCredentials = Full. The two are configured independently — check that '\n + \"sparkAuthRoutes({ localCredentials: … }) matches AddAuthentication's LocalCredentials mode.\",\n );\n }\n }\n\n signInWith(provider: SparkExternalProvider): void {\n void this.authService.loginWithProvider(provider.scheme);\n }\n}\n\nexport default SparkSignInComponent;\n","<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.login' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (loading()) {\n <div class=\"text-center\"><bs-spinner /></div>\n } @else if (failed()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-0\">{{ 'auth.signInUnavailable' | t }}</bs-alert>\n } @else {\n @for (provider of providers(); track provider.scheme) {\n <button type=\"button\" class=\"btn btn-outline-primary w-100 mb-2\" (click)=\"signInWith(provider)\">\n {{ provider.displayName }}\n </button>\n } @empty {\n <bs-alert [type]=\"colors.warning\" class=\"mb-0\">{{ 'auth.noSignInMethods' | t }}</bs-alert>\n }\n\n @if (localCredentialsAvailable()) {\n <div class=\"text-center mt-3\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.login' | t }}</a>\n </div>\n }\n }\n </div>\n </bs-card>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -65,7 +65,7 @@ class SparkTwoFactorComponent {
65
65
  }
66
66
  }
67
67
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SparkTwoFactorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
68
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: SparkTwoFactorComponent, isStandalone: true, selector: "spark-two-factor", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.twoFactorTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @if (!useRecoveryCode()) {\n <div class=\"mb-3\">\n <label for=\"code\" class=\"form-label\">{{ 'auth.code' | t }}</label>\n <input\n type=\"text\"\n id=\"code\"\n formControlName=\"code\"\n autocomplete=\"one-time-code\"\n maxlength=\"6\"\n [placeholder]=\"'auth.enterCode' | t\"\n />\n </div>\n } @else {\n <div class=\"mb-3\">\n <label for=\"recoveryCode\" class=\"form-label\">{{ 'auth.recoveryCode' | t }}</label>\n <input\n type=\"text\"\n id=\"recoveryCode\"\n formControlName=\"recoveryCode\"\n autocomplete=\"off\"\n [placeholder]=\"'auth.enterRecoveryCode' | t\"\n />\n </div>\n }\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.verify' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <button class=\"btn btn-link\" (click)=\"toggleRecoveryCode()\">\n @if (useRecoveryCode()) {\n {{ 'auth.useAuthCode' | t }}\n } @else {\n {{ 'auth.useRecoveryCode' | t }}\n }\n </button>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.backToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["color", "outline"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["color", "navStyle"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
68
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: SparkTwoFactorComponent, isStandalone: true, selector: "spark-two-factor", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.twoFactorTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @if (!useRecoveryCode()) {\n <div class=\"mb-3\">\n <label for=\"code\" class=\"form-label\">{{ 'auth.code' | t }}</label>\n <input\n type=\"text\"\n id=\"code\"\n formControlName=\"code\"\n autocomplete=\"one-time-code\"\n maxlength=\"6\"\n [placeholder]=\"'auth.enterCode' | t\"\n />\n </div>\n } @else {\n <div class=\"mb-3\">\n <label for=\"recoveryCode\" class=\"form-label\">{{ 'auth.recoveryCode' | t }}</label>\n <input\n type=\"text\"\n id=\"recoveryCode\"\n formControlName=\"recoveryCode\"\n autocomplete=\"off\"\n [placeholder]=\"'auth.enterRecoveryCode' | t\"\n />\n </div>\n }\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.verify' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <button class=\"btn btn-link\" (click)=\"toggleRecoveryCode()\">\n @if (useRecoveryCode()) {\n {{ 'auth.useAuthCode' | t }}\n } @else {\n {{ 'auth.useRecoveryCode' | t }}\n }\n </button>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.backToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "announce", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["color", "outline"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["color", "navStyle"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
69
69
  }
70
70
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SparkTwoFactorComponent, decorators: [{
71
71
  type: Component,
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-auth-two-factor.mjs","sources":["../../two-factor/src/spark-two-factor.component.ts","../../two-factor/src/spark-two-factor.component.html","../../two-factor/mintplayer-ng-spark-auth-two-factor.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';\nimport { RouterLink, ActivatedRoute, Router } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SPARK_AUTH_CONFIG, SPARK_AUTH_ROUTE_PATHS, sanitizeReturnUrl } from '@mintplayer/ng-spark-auth/models';\nimport { SparkAuthService, SparkAuthTranslationService } from '@mintplayer/ng-spark-auth/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark-auth/pipes';\n\n@Component({\n selector: 'spark-two-factor',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe],\n templateUrl: './spark-two-factor.component.html',\n})\nexport class SparkTwoFactorComponent {\n private readonly fb = inject(FormBuilder);\n private readonly authService = inject(SparkAuthService);\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly config = inject(SPARK_AUTH_CONFIG);\n private readonly translation = inject(SparkAuthTranslationService);\n readonly routePaths = inject(SPARK_AUTH_ROUTE_PATHS);\n\n colors = Color;\n readonly loading = signal(false);\n readonly errorMessage = signal('');\n readonly useRecoveryCode = signal(false);\n\n readonly form = this.fb.group({\n code: [''],\n recoveryCode: [''],\n });\n\n toggleRecoveryCode(): void {\n this.useRecoveryCode.update(v => !v);\n this.errorMessage.set('');\n }\n\n async onSubmit(): Promise<void> {\n const isRecovery = this.useRecoveryCode();\n const code = isRecovery ? this.form.value.recoveryCode : this.form.value.code;\n\n if (!code?.trim()) {\n this.errorMessage.set(isRecovery\n ? this.translation.t('auth.enterRecoveryCodeError')\n : this.translation.t('auth.enterCodeError'));\n return;\n }\n\n this.loading.set(true);\n this.errorMessage.set('');\n\n const twoFactorCode = isRecovery ? undefined : code;\n const twoFactorRecoveryCode = isRecovery ? code : undefined;\n\n try {\n await this.authService.loginTwoFactor(twoFactorCode ?? '', twoFactorRecoveryCode);\n // R2-H9: same sanitization as login. The 2FA component receives returnUrl\n // forwarded from the login step but must still validate — query params\n // travel through router state and a deep-link to /two-factor?returnUrl=…\n // is a viable attacker vector.\n const returnUrl = sanitizeReturnUrl(\n this.route.snapshot.queryParamMap.get('returnUrl'),\n this.config.defaultRedirectUrl);\n this.router.navigateByUrl(returnUrl);\n } catch {\n this.errorMessage.set(this.translation.t('auth.invalidCode'));\n } finally {\n this.loading.set(false);\n }\n }\n}\n\nexport default SparkTwoFactorComponent;\n","<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.twoFactorTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @if (!useRecoveryCode()) {\n <div class=\"mb-3\">\n <label for=\"code\" class=\"form-label\">{{ 'auth.code' | t }}</label>\n <input\n type=\"text\"\n id=\"code\"\n formControlName=\"code\"\n autocomplete=\"one-time-code\"\n maxlength=\"6\"\n [placeholder]=\"'auth.enterCode' | t\"\n />\n </div>\n } @else {\n <div class=\"mb-3\">\n <label for=\"recoveryCode\" class=\"form-label\">{{ 'auth.recoveryCode' | t }}</label>\n <input\n type=\"text\"\n id=\"recoveryCode\"\n formControlName=\"recoveryCode\"\n autocomplete=\"off\"\n [placeholder]=\"'auth.enterRecoveryCode' | t\"\n />\n </div>\n }\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.verify' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <button class=\"btn btn-link\" (click)=\"toggleRecoveryCode()\">\n @if (useRecoveryCode()) {\n {{ 'auth.useAuthCode' | t }}\n } @else {\n {{ 'auth.useRecoveryCode' | t }}\n }\n </button>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.backToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;;;MAmBa,uBAAuB,CAAA;AACjB,IAAA,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;AACxB,IAAA,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC;AACtC,IAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AACvB,IAAA,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC;AAC9B,IAAA,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC;AAClC,IAAA,WAAW,GAAG,MAAM,CAAC,2BAA2B,CAAC;AACzD,IAAA,UAAU,GAAG,MAAM,CAAC,sBAAsB,CAAC;IAEpD,MAAM,GAAG,KAAK;IACL,OAAO,GAAG,MAAM,CAAC,KAAK;gFAAC;IACvB,YAAY,GAAG,MAAM,CAAC,EAAE;qFAAC;IACzB,eAAe,GAAG,MAAM,CAAC,KAAK;wFAAC;AAE/B,IAAA,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;QAC5B,IAAI,EAAE,CAAC,EAAE,CAAC;QACV,YAAY,EAAE,CAAC,EAAE,CAAC;AACnB,KAAA,CAAC;IAEF,kBAAkB,GAAA;AAChB,QAAA,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACpC,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3B;AAEA,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE;QACzC,MAAM,IAAI,GAAG,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI;AAE7E,QAAA,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;AACjB,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC;kBAClB,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,6BAA6B;kBAChD,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC;YAC9C;QACF;AAEA,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;QAEzB,MAAM,aAAa,GAAG,UAAU,GAAG,SAAS,GAAG,IAAI;QACnD,MAAM,qBAAqB,GAAG,UAAU,GAAG,IAAI,GAAG,SAAS;AAE3D,QAAA,IAAI;AACF,YAAA,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,aAAa,IAAI,EAAE,EAAE,qBAAqB,CAAC;;;;;YAKjF,MAAM,SAAS,GAAG,iBAAiB,CACjC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,EAClD,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;AACjC,YAAA,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC;QACtC;AAAE,QAAA,MAAM;AACN,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;QAC/D;gBAAU;AACR,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;QACzB;IACF;uGAxDW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAvB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,uBAAuB,4ECnBpC,4nEAiEA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDjDY,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,aAAA,EAAA,QAAA,EAAA,8CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,wSAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,sGAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,kBAAA,EAAA,QAAA,EAAA,4EAAA,EAAA,MAAA,EAAA,CAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,kBAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,CAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,UAAA,CAAA,EAAA,QAAA,EAAA,CAAA,QAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,MAAA,EAAA,CAAA,iBAAA,EAAA,UAAA,EAAA,SAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,UAAU,kPAAE,gBAAgB,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,iBAAA,EAAA,qBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,eAAe,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,qBAAqB,0FAAE,eAAe,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,QAAA,CAAA,EAAA,OAAA,EAAA,CAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,sBAAsB,EAAA,QAAA,EAAA,6EAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,kBAAkB,6EAAE,gBAAgB,EAAA,IAAA,EAAA,GAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAGvK,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAPnC,SAAS;+BACE,kBAAkB,EAAA,UAAA,EAChB,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC,CAAC,mBAAmB,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,eAAe,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,gBAAgB,CAAC,EAAA,QAAA,EAAA,4nEAAA,EAAA;;;AEhBrL;;AAEG;;;;"}
1
+ {"version":3,"file":"mintplayer-ng-spark-auth-two-factor.mjs","sources":["../../two-factor/src/spark-two-factor.component.ts","../../two-factor/src/spark-two-factor.component.html","../../two-factor/mintplayer-ng-spark-auth-two-factor.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';\nimport { RouterLink, ActivatedRoute, Router } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SPARK_AUTH_CONFIG, SPARK_AUTH_ROUTE_PATHS, sanitizeReturnUrl } from '@mintplayer/ng-spark-auth/models';\nimport { SparkAuthService, SparkAuthTranslationService } from '@mintplayer/ng-spark-auth/core';\nimport { TranslateKeyPipe } from '@mintplayer/ng-spark-auth/pipes';\n\n@Component({\n selector: 'spark-two-factor',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe],\n templateUrl: './spark-two-factor.component.html',\n})\nexport class SparkTwoFactorComponent {\n private readonly fb = inject(FormBuilder);\n private readonly authService = inject(SparkAuthService);\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly config = inject(SPARK_AUTH_CONFIG);\n private readonly translation = inject(SparkAuthTranslationService);\n readonly routePaths = inject(SPARK_AUTH_ROUTE_PATHS);\n\n colors = Color;\n readonly loading = signal(false);\n readonly errorMessage = signal('');\n readonly useRecoveryCode = signal(false);\n\n readonly form = this.fb.group({\n code: [''],\n recoveryCode: [''],\n });\n\n toggleRecoveryCode(): void {\n this.useRecoveryCode.update(v => !v);\n this.errorMessage.set('');\n }\n\n async onSubmit(): Promise<void> {\n const isRecovery = this.useRecoveryCode();\n const code = isRecovery ? this.form.value.recoveryCode : this.form.value.code;\n\n if (!code?.trim()) {\n this.errorMessage.set(isRecovery\n ? this.translation.t('auth.enterRecoveryCodeError')\n : this.translation.t('auth.enterCodeError'));\n return;\n }\n\n this.loading.set(true);\n this.errorMessage.set('');\n\n const twoFactorCode = isRecovery ? undefined : code;\n const twoFactorRecoveryCode = isRecovery ? code : undefined;\n\n try {\n await this.authService.loginTwoFactor(twoFactorCode ?? '', twoFactorRecoveryCode);\n // R2-H9: same sanitization as login. The 2FA component receives returnUrl\n // forwarded from the login step but must still validate — query params\n // travel through router state and a deep-link to /two-factor?returnUrl=…\n // is a viable attacker vector.\n const returnUrl = sanitizeReturnUrl(\n this.route.snapshot.queryParamMap.get('returnUrl'),\n this.config.defaultRedirectUrl);\n this.router.navigateByUrl(returnUrl);\n } catch {\n this.errorMessage.set(this.translation.t('auth.invalidCode'));\n } finally {\n this.loading.set(false);\n }\n }\n}\n\nexport default SparkTwoFactorComponent;\n","<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'auth.twoFactorTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @if (!useRecoveryCode()) {\n <div class=\"mb-3\">\n <label for=\"code\" class=\"form-label\">{{ 'auth.code' | t }}</label>\n <input\n type=\"text\"\n id=\"code\"\n formControlName=\"code\"\n autocomplete=\"one-time-code\"\n maxlength=\"6\"\n [placeholder]=\"'auth.enterCode' | t\"\n />\n </div>\n } @else {\n <div class=\"mb-3\">\n <label for=\"recoveryCode\" class=\"form-label\">{{ 'auth.recoveryCode' | t }}</label>\n <input\n type=\"text\"\n id=\"recoveryCode\"\n formControlName=\"recoveryCode\"\n autocomplete=\"off\"\n [placeholder]=\"'auth.enterRecoveryCode' | t\"\n />\n </div>\n }\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'auth.verify' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <button class=\"btn btn-link\" (click)=\"toggleRecoveryCode()\">\n @if (useRecoveryCode()) {\n {{ 'auth.useAuthCode' | t }}\n } @else {\n {{ 'auth.useRecoveryCode' | t }}\n }\n </button>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'auth.backToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mintplayer/ng-spark-auth",
3
3
  "private": false,
4
- "version": "22.0.1",
4
+ "version": "22.2.0",
5
5
  "description": "Angular authentication library for MintPlayer.Spark",
6
6
  "repository": {
7
7
  "type": "git",
@@ -71,6 +71,10 @@
71
71
  "types": "./types/mintplayer-ng-spark-auth-routes.d.ts",
72
72
  "default": "./fesm2022/mintplayer-ng-spark-auth-routes.mjs"
73
73
  },
74
+ "./sign-in": {
75
+ "types": "./types/mintplayer-ng-spark-auth-sign-in.d.ts",
76
+ "default": "./fesm2022/mintplayer-ng-spark-auth-sign-in.mjs"
77
+ },
74
78
  "./two-factor": {
75
79
  "types": "./types/mintplayer-ng-spark-auth-two-factor.d.ts",
76
80
  "default": "./fesm2022/mintplayer-ng-spark-auth-two-factor.mjs"
@@ -6,6 +6,8 @@ declare class SparkAuthBarComponent {
6
6
  readonly authService: SparkAuthService;
7
7
  readonly config: _mintplayer_ng_spark_auth_models.SparkAuthConfig;
8
8
  private readonly router;
9
+ /** Same target the guard and interceptor use, so the bar cannot link somewhere they would not send you. */
10
+ readonly signInUrl: string;
9
11
  onLogout(): Promise<void>;
10
12
  static ɵfac: i0.ɵɵFactoryDeclaration<SparkAuthBarComponent, never>;
11
13
  static ɵcmp: i0.ɵɵComponentDeclaration<SparkAuthBarComponent, "spark-auth-bar", never, {}, {}, never, never, true, never>;
@@ -1,9 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { AuthUser } from '@mintplayer/ng-spark-auth/models';
2
+ import { AuthUser, SparkAuthCapabilities, SparkExternalLoginOptions, SparkExternalLoginResult } from '@mintplayer/ng-spark-auth/models';
3
3
 
4
4
  declare class SparkAuthService {
5
5
  private readonly http;
6
6
  private readonly config;
7
+ private readonly zone;
7
8
  private readonly currentUser;
8
9
  readonly user: i0.Signal<AuthUser | null>;
9
10
  readonly isAuthenticated: i0.Signal<boolean>;
@@ -12,8 +13,29 @@ declare class SparkAuthService {
12
13
  loginTwoFactor(twoFactorCode: string, twoFactorRecoveryCode?: string): Promise<void>;
13
14
  register(email: string, password: string): Promise<void>;
14
15
  logout(): Promise<void>;
16
+ /**
17
+ * What sign-in methods this deployment actually offers.
18
+ *
19
+ * Read from the server rather than assumed from the client's own route configuration: the two are
20
+ * configured independently, and a mismatch is otherwise invisible until a user hits it.
21
+ */
22
+ capabilities(): Promise<SparkAuthCapabilities>;
15
23
  csrfRefresh(): Promise<void>;
16
24
  checkAuth(): Promise<AuthUser | null>;
25
+ /**
26
+ * Signs in through an external provider (GitHub, Google, …) and leaves this service's
27
+ * `user()` signal up to date, exactly as `login()` does.
28
+ *
29
+ * The whole handshake lives here: opening the window, listening for the callback's
30
+ * message, checking its origin, detecting a popup the user closed by hand, tearing the
31
+ * listener and poll down on every exit path, and re-reading the session afterwards.
32
+ * Callers get an outcome, not a protocol — which is the point, because the previous
33
+ * hand-rolled version leaked its listener whenever the user simply closed the window.
34
+ *
35
+ * In `'redirect'` mode the returned promise never settles: this document is being
36
+ * replaced, and the outcome arrives as the next page load rather than as a value.
37
+ */
38
+ loginWithProvider(provider: string, options?: SparkExternalLoginOptions): Promise<SparkExternalLoginResult>;
17
39
  forgotPassword(email: string): Promise<void>;
18
40
  resetPassword(email: string, resetCode: string, newPassword: string): Promise<void>;
19
41
  static ɵfac: i0.ɵɵFactoryDeclaration<SparkAuthService, never>;
@@ -7,7 +7,7 @@ declare class SparkForgotPasswordComponent {
7
7
  private readonly fb;
8
8
  private readonly authService;
9
9
  private readonly translation;
10
- readonly routePaths: Required<Record<keyof _mintplayer_ng_spark_auth_models.SparkAuthRouteConfig, string>>;
10
+ readonly routePaths: Required<Record<keyof _mintplayer_ng_spark_auth_models.SparkAuthRouteEntries, string>>;
11
11
  colors: typeof Color;
12
12
  readonly loading: _angular_core.WritableSignal<boolean>;
13
13
  readonly errorMessage: _angular_core.WritableSignal<string>;
@@ -10,7 +10,7 @@ declare class SparkLoginComponent {
10
10
  private readonly route;
11
11
  private readonly config;
12
12
  private readonly translation;
13
- readonly routePaths: Required<Record<keyof _mintplayer_ng_spark_auth_models.SparkAuthRouteConfig, string>>;
13
+ readonly routePaths: Required<Record<keyof _mintplayer_ng_spark_auth_models.SparkAuthRouteEntries, string>>;
14
14
  colors: typeof Color;
15
15
  readonly loading: _angular_core.WritableSignal<boolean>;
16
16
  readonly errorMessage: _angular_core.WritableSignal<string>;