Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions eslint.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* eslint-disable @typescript-eslint/no-explicit-any */

import js from "@eslint/js";
Expand Down
16 changes: 16 additions & 0 deletions packages/angular/jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import type { Config } from "jest";
import { createCjsPreset } from "jest-preset-angular/presets/index.js";

Expand Down
8 changes: 8 additions & 0 deletions packages/angular/src/lib/auth/forms/email-link-auth-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ import { injectEmailLinkAuthFormSchema, injectTranslation, injectUI } from "../.
}
`,
})
/**
* A form component for email link authentication.
*
* Sends a sign-in link to the user's email address and automatically completes sign-in
* if the user arrives via an email link.
*/
export class EmailLinkAuthFormComponent {
private ui = injectUI();
private formSchema = injectEmailLinkAuthFormSchema();
Expand All @@ -75,7 +81,9 @@ export class EmailLinkAuthFormComponent {
emailSentMessage = injectTranslation("messages", "signInLinkSent");
unknownErrorLabel = injectTranslation("errors", "unknownError");

/** Event emitter fired when sign-in link email is sent. */
@Output() emailSent = new EventEmitter<void>();
/** Event emitter for successful sign-in. */
@Output() signIn = new EventEmitter<UserCredential>();

form = injectForm({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ import { injectForgotPasswordAuthFormSchema, injectTranslation, injectUI } from
}
`,
})
/**
* A form component for requesting a password reset email.
*
* Displays a success message after the email is sent.
*/
export class ForgotPasswordAuthFormComponent {
private ui = injectUI();
private formSchema = injectForgotPasswordAuthFormSchema();
Expand All @@ -85,8 +90,10 @@ export class ForgotPasswordAuthFormComponent {
checkEmailForResetMessage = injectTranslation("messages", "checkEmailForReset");
unknownErrorLabel = injectTranslation("errors", "unknownError");

/** Event emitter for back to sign in action. */
backToSignIn = input<EventEmitter<void>>();

/** Event emitter fired when password reset email is sent. */
@Output() passwordSent = new EventEmitter<void>();

form = injectForm({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,15 @@ type PhoneMultiFactorInfo = MultiFactorInfo & {
</form>
`,
})
/**
* A form component for requesting SMS verification code during MFA assertion.
*/
export class SmsMultiFactorAssertionPhoneFormComponent {
private ui = injectUI();

/** The multi-factor info hint containing phone number details. */
hint = input.required<MultiFactorInfo>();
/** Event emitter fired when verification ID is received. */
@Output() onSubmit = new EventEmitter<string>();

sendCodeLabel = injectTranslation("labels", "sendCode");
Expand Down Expand Up @@ -164,11 +169,16 @@ export class SmsMultiFactorAssertionPhoneFormComponent {
</form>
`,
})
/**
* A form component for verifying SMS code during MFA assertion.
*/
export class SmsMultiFactorAssertionVerifyFormComponent {
private ui = injectUI();
private formSchema = injectMultiFactorPhoneAuthVerifyFormSchema();

/** The verification ID received from the phone form. */
verificationId = input.required<string>();
/** Event emitter for successful MFA assertion. */
@Output() onSuccess = new EventEmitter<UserCredential>();

verificationCodeLabel = injectTranslation("labels", "verificationCode");
Expand Down Expand Up @@ -238,8 +248,15 @@ export class SmsMultiFactorAssertionVerifyFormComponent {
</div>
`,
})
/**
* A form component for SMS multi-factor authentication assertion.
*
* Manages the flow between requesting and verifying SMS codes for MFA.
*/
export class SmsMultiFactorAssertionFormComponent {
/** The multi-factor info hint containing phone number details. */
hint = input.required<MultiFactorInfo>();
/** Event emitter for successful MFA assertion. */
@Output() onSuccess = new EventEmitter<UserCredential>();

verification = signal<{ verificationId: string } | null>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ import {
</div>
`,
})
/**
* A form component for SMS multi-factor authentication enrollment.
*
* Manages the flow between phone number entry and verification code entry for MFA enrollment.
*/
export class SmsMultiFactorEnrollmentFormComponent {
private ui = injectUI();
private phoneFormSchema = injectMultiFactorPhoneAuthNumberFormSchema();
Expand All @@ -128,6 +133,7 @@ export class SmsMultiFactorEnrollmentFormComponent {
verifyCodeLabel = injectTranslation("labels", "verifyCode");
smsVerificationPrompt = injectTranslation("prompts", "smsVerificationPrompt");

/** Event emitter fired when MFA enrollment is completed. */
@Output() onEnrollment = new EventEmitter<void>();

recaptchaContainer = viewChild.required<ElementRef<HTMLDivElement>>("recaptchaContainer");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,18 @@ import { TotpMultiFactorGenerator, type MultiFactorInfo, type UserCredential } f
</form>
`,
})
/**
* A form component for TOTP multi-factor authentication assertion.
*
* Allows users to enter a TOTP code from their authenticator app.
*/
export class TotpMultiFactorAssertionFormComponent {
private ui = injectUI();
private formSchema = injectMultiFactorTotpAuthVerifyFormSchema();

/** The multi-factor info hint containing TOTP details. */
hint = input.required<MultiFactorInfo>();
/** Event emitter for successful MFA assertion. */
@Output() onSuccess = new EventEmitter<UserCredential>();

verificationCodeLabel = injectTranslation("labels", "verificationCode");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ import {
</form>
`,
})
/**
* A form component for generating a TOTP secret and display name during MFA enrollment.
*/
export class TotpMultiFactorSecretGenerationFormComponent {
private ui = injectUI();
private formSchema = injectMultiFactorTotpAuthNumberFormSchema();

/** Event emitter fired when TOTP secret is generated. */
@Output() onSubmit = new EventEmitter<{ secret: TotpSecret; displayName: string }>();

displayNameLabel = injectTranslation("labels", "displayName");
Expand Down Expand Up @@ -150,12 +154,20 @@ export class TotpMultiFactorSecretGenerationFormComponent {
</form>
`,
})
/**
* A form component for verifying TOTP code during MFA enrollment.
*
* Displays a QR code and allows users to verify their authenticator app setup.
*/
export class TotpMultiFactorVerificationFormComponent {
private ui = injectUI();
private formSchema = injectMultiFactorTotpAuthVerifyFormSchema();

/** The TOTP secret generated in the previous step. */
secret = input.required<TotpSecret>();
/** The display name for the TOTP factor. */
displayName = input.required<string>();
/** Event emitter fired when MFA enrollment is completed. */
@Output() onEnrollment = new EventEmitter<void>();

verificationCodeLabel = injectTranslation("labels", "verificationCode");
Expand Down Expand Up @@ -224,10 +236,16 @@ export class TotpMultiFactorVerificationFormComponent {
</div>
`,
})
/**
* A form component for TOTP multi-factor authentication enrollment.
*
* Manages the flow between secret generation and verification for TOTP MFA enrollment.
*/
export class TotpMultiFactorEnrollmentFormComponent {
private ui = injectUI();

enrollment = signal<{ secret: TotpSecret; displayName: string } | null>(null);
/** Event emitter fired when MFA enrollment is completed. */
@Output() onEnrollment = new EventEmitter<void>();

constructor() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ import { ButtonComponent } from "../../components/button";
</div>
`,
})
/**
* A form component for multi-factor authentication assertion.
*
* Allows users to select and complete MFA verification using SMS or TOTP.
*/
export class MultiFactorAuthAssertionFormComponent {
private ui = injectUI();

Expand All @@ -71,6 +76,7 @@ export class MultiFactorAuthAssertionFormComponent {
});
}

/** Event emitter for successful MFA assertion. */
@Output() onSuccess = new EventEmitter<UserCredential>();

resolver = computed(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,15 @@ type Hint = (typeof FactorId)[keyof typeof FactorId];
</div>
`,
})
/**
* A form component for multi-factor authentication enrollment.
*
* Allows users to enroll in MFA using SMS or TOTP methods.
*/
export class MultiFactorAuthEnrollmentFormComponent implements OnInit {
/** The available MFA factor types for enrollment. */
hints = input<Hint[]>([FactorId.TOTP, FactorId.PHONE]);
/** Event emitter fired when MFA enrollment is completed. */
@Output() onEnrollment = new EventEmitter<void>();

selectedHint = signal<Hint | undefined>(undefined);
Expand Down
16 changes: 16 additions & 0 deletions packages/angular/src/lib/auth/forms/phone-auth-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,16 @@ import {
</form>
`,
})
/**
* A form component for entering a phone number and requesting a verification code.
*/
export class PhoneNumberFormComponent {
private ui = injectUI();
private formSchema = injectPhoneAuthFormSchema();

/** Event emitter fired when phone number is verified and verification ID is received. */
@Output() onSubmit = new EventEmitter<{ verificationId: string; phoneNumber: string }>();
/** The selected country code for phone number formatting. */
country = signal<CountryCode>(countryData[0].code);

phoneNumberLabel = injectTranslation("labels", "phoneNumber");
Expand Down Expand Up @@ -187,11 +192,16 @@ export class PhoneNumberFormComponent {
</form>
`,
})
/**
* A form component for entering and verifying the SMS verification code.
*/
export class VerificationFormComponent {
private ui = injectUI();
private formSchema = injectPhoneAuthVerifyFormSchema();

/** The verification ID received from the phone number form. */
verificationId = input.required<string>();
/** Event emitter for successful sign-in. */
@Output() signIn = new EventEmitter<UserCredential>();

verificationCodeLabel = injectTranslation("labels", "verificationCode");
Expand Down Expand Up @@ -259,8 +269,14 @@ export class VerificationFormComponent {
</div>
`,
})
/**
* A form component for phone number authentication.
*
* Manages the flow between phone number entry and verification code entry.
*/
export class PhoneAuthFormComponent {
verificationId = signal<string | null>(null);
/** Event emitter for successful sign-in. */
@Output() signIn = new EventEmitter<UserCredential>();

handlePhoneSubmit(data: { verificationId: string; phoneNumber: string }) {
Expand Down
6 changes: 6 additions & 0 deletions packages/angular/src/lib/auth/forms/sign-in-auth-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ import {
</form>
`,
})
/**
* A form component for signing in with email and password.
*/
export class SignInAuthFormComponent {
private ui = injectUI();
private formSchema = injectSignInAuthFormSchema();
Expand All @@ -99,9 +102,12 @@ export class SignInAuthFormComponent {
signUpLabel = injectTranslation("labels", "signUp");
unknownErrorLabel = injectTranslation("errors", "unknownError");

/** Event emitter for forgot password action. */
forgotPassword = input<EventEmitter<void>>();
/** Event emitter for sign up action. */
signUp = input<EventEmitter<void>>();

/** Event emitter for successful sign-in. */
@Output() signIn = new EventEmitter<UserCredential>();

form = injectForm({
Expand Down
7 changes: 7 additions & 0 deletions packages/angular/src/lib/auth/forms/sign-up-auth-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ import {
</form>
`,
})
/**
* A form component for signing up with email and password.
*
* Optionally includes a display name field if the requireDisplayName behavior is enabled.
*/
export class SignUpAuthFormComponent {
private ui = injectUI();
private formSchema = injectSignUpAuthFormSchema();
Expand All @@ -94,8 +99,10 @@ export class SignUpAuthFormComponent {
signInLabel = injectTranslation("labels", "signIn");
unknownErrorLabel = injectTranslation("errors", "unknownError");

/** Event emitter for sign in action. */
signIn = input<EventEmitter<void>>();

/** Event emitter for successful sign-up. */
@Output() signUp = new EventEmitter<UserCredential>();

form = injectForm({
Expand Down
6 changes: 6 additions & 0 deletions packages/angular/src/lib/auth/oauth/apple-sign-in-button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,20 @@ import { AppleLogoComponent } from "../../components/logos/apple";
</fui-oauth-button>
`,
})
/**
* A button component for signing in with Apple.
*/
export class AppleSignInButtonComponent {
ui = injectUI();
signInWithAppleLabel = injectTranslation("labels", "signInWithApple");
/** Whether to use themed styling. */
themed = input<boolean>(false);
/** Event emitter for successful sign-in. */
signIn = output<UserCredential>();

private defaultProvider = new OAuthProvider("apple.com");

/** Optional custom OAuth provider configuration. */
provider = input<OAuthProvider>();

get appleProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,20 @@ import { FacebookLogoComponent } from "../../components/logos/facebook";
</fui-oauth-button>
`,
})
/**
* A button component for signing in with Facebook.
*/
export class FacebookSignInButtonComponent {
ui = injectUI();
signInWithFacebookLabel = injectTranslation("labels", "signInWithFacebook");
/** Whether to use themed styling. */
themed = input<boolean>(false);
/** Event emitter for successful sign-in. */
signIn = output<UserCredential>();

private defaultProvider = new FacebookAuthProvider();

/** Optional custom OAuth provider configuration. */
provider = input<FacebookAuthProvider>();

get facebookProvider() {
Expand Down
Loading