Skip to content

Commit

Permalink
Login with Microsoft (#1744)
Browse files Browse the repository at this point in the history
- feat(Microsoft Login): Register and login Teacher (#1713)
- feat(Microsoft Login): Register and Login Student (#1717)
- refactor(Register User): Extract AbstractRegisterUserComponent (#1719)

Co-authored-by: Jonathan Lim-Breitbart <breity10@gmail.com>
  • Loading branch information
hirokiterashima and breity authored Apr 19, 2024
1 parent 590b3a5 commit 23f8208
Show file tree
Hide file tree
Showing 29 changed files with 381 additions and 123 deletions.
1 change: 1 addition & 0 deletions src/app/domain/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export class Config {
googleAnalyticsId?: string;
googleClientId?: string;
isGoogleClassroomEnabled?: boolean;
microsoftClientId?: string;
recaptchaPublicKey?: string;
logOutURL: string;
currentTime: number;
Expand Down
1 change: 1 addition & 0 deletions src/app/domain/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export class User {
isRecaptchaRequired: boolean;
language: string;
lastName: string;
microsoftUserId: string;
permissions: number[];
roles: string[];
token: string;
Expand Down
15 changes: 13 additions & 2 deletions src/app/login/login-home/login-home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ <h2 class="standalone__title accent" i18n>Sign in to WISE</h2>
</button>
</p>
</form>
<div *ngIf="isGoogleAuthenticationEnabled && isShowGoogleLogin">
<div *ngIf="showSocialLogin">
<p class="center mat-headline-6">- <span i18>or</span> -</p>
<p class="center">
<p class="center" *ngIf="googleAuthenticationEnabled">
<button
class="button--social-login button--google"
color="accent"
Expand All @@ -73,6 +73,17 @@ <h2 class="standalone__title accent" i18n>Sign in to WISE</h2>
<ng-container i18n>Sign in with Google</ng-container>
</button>
</p>
<p class="center" *ngIf="microsoftAuthenticationEnabled">
<button
class="button--social-login button--microsoft"
color="accent"
mat-flat-button
(click)="socialSignIn('microsoft')"
>
<img src="assets/img/icons/ms-logo.svg" alt="Microsoft logo" />
<ng-container i18n>Sign in with Microsoft</ng-container>
</button>
</p>
</div>
</mat-card-content>
<mat-divider></mat-divider>
Expand Down
14 changes: 10 additions & 4 deletions src/app/login/login-home/login-home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import { lastValueFrom } from 'rxjs';
export class LoginHomeComponent implements OnInit {
accessCode: string = '';
credentials: any = { username: '', password: '', recaptchaResponse: null };
isGoogleAuthenticationEnabled: boolean = false;
protected googleAuthenticationEnabled: boolean = false;
isRecaptchaEnabled: boolean = false;
isRecaptchaVerificationFailed: boolean = false;
isReLoginDueToErrorSavingData: boolean;
isShowGoogleLogin: boolean = true;
protected microsoftAuthenticationEnabled: boolean;
passwordError: boolean = false;
processing: boolean = false;
@ViewChild('recaptchaRef', { static: false }) recaptchaRef: any;
protected showSocialLogin: boolean;

constructor(
private configService: ConfigService,
Expand All @@ -33,16 +34,19 @@ export class LoginHomeComponent implements OnInit {
ngOnInit(): void {
this.configService.getConfig().subscribe((config) => {
if (config != null) {
this.isGoogleAuthenticationEnabled = config.googleClientId != '';
this.googleAuthenticationEnabled = config.googleClientId != '';
this.microsoftAuthenticationEnabled = config.microsoftClientId != '';
}
if (this.userService.isSignedIn()) {
this.router.navigateByUrl(this.getRedirectUrl(''));
}
this.showSocialLogin =
this.googleAuthenticationEnabled || this.microsoftAuthenticationEnabled;
});
this.route.params.subscribe((params) => {
if (params['username'] != null) {
this.credentials.username = params['username'];
this.isShowGoogleLogin = false;
this.showSocialLogin = false;
}
});
this.route.queryParams.subscribe((params) => {
Expand Down Expand Up @@ -97,6 +101,8 @@ export class LoginHomeComponent implements OnInit {
let redirectUrl = '';
if (social === 'google') {
redirectUrl = `${this.configService.getContextPath()}/api/google-login?redirectUrl=${this.userService.getRedirectUrl()}`;
} else if (social === 'microsoft') {
redirectUrl = `/api/microsoft-login?redirectUrl=/`;
} else {
redirectUrl = this.userService.getRedirectUrl();
}
Expand Down
53 changes: 53 additions & 0 deletions src/app/register/abstract-register-user.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Router } from '@angular/router';
import { UserService } from '../services/user.service';
import { ConfigService } from '../services/config.service';
import { Directive, OnInit } from '@angular/core';
import { GoogleUser } from '../modules/google-sign-in/GoogleUser';

@Directive()
export abstract class AbstractRegisterUserComponent implements OnInit {
protected googleAuthenticationEnabled: boolean = false;
protected abstract joinFormPath: string;
protected microsoftAuthenticationEnabled: boolean = false;

constructor(
private configService: ConfigService,
private router: Router,
private userService: UserService
) {}

ngOnInit(): void {
this.configService.getConfig().subscribe((config) => {
if (config != null) {
this.googleAuthenticationEnabled = this.isSet(config.googleClientId);
this.microsoftAuthenticationEnabled = this.isSet(config.microsoftClientId);
}
});
}

private isSet(value: string): boolean {
return value != null && value != '';
}

protected googleSignIn(credential: GoogleUser): void {
this.userService.isGoogleIdExists(credential.sub).subscribe((isExists) => {
if (isExists) {
this.router.navigate(['join/googleUserAlreadyExists']);
} else {
this.router.navigate([this.joinFormPath, this.getGoogleFormParams(credential)]);
}
});
}

protected signUp(): void {
this.router.navigate([this.joinFormPath, this.getFormParams()]);
}

protected microsoftSignIn(): void {
window.location.href = `/api/microsoft-login?redirectUrl=${this.joinFormPath}`;
}

protected abstract getFormParams(): any;

protected abstract getGoogleFormParams(credential: GoogleUser): any;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<mat-card-content>
<h2 class="standalone__title accent" i18n>Create Account</h2>
<p class="mat-subtitle-2" i18n>Hi! This Google user already has a WISE account.</p>
<mat-card-actions>
<mat-card-actions fxLayout="row" fxLayoutAlign="center center">
<button
class="button--social-login button--google"
color="accent"
Expand Down
3 changes: 3 additions & 0 deletions src/app/register/register-home/register-home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
<p *ngIf="googleUserNotFoundError" class="mat-subtitle-1 warn center" i18n>
Sorry, your Google User ID was not found in WISE.
</p>
<p *ngIf="microsoftUserNotFoundError" class="mat-subtitle-1 warn center" i18n>
Sorry, your Microsoft User ID was not found in WISE.
</p>
<h1 class="standalone__title accent" i18n>Create WISE Account</h1>
<div
class="standalone__content register"
Expand Down
2 changes: 2 additions & 0 deletions src/app/register/register-home/register-home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import { ActivatedRoute } from '@angular/router';
})
export class RegisterHomeComponent implements OnInit {
googleUserNotFoundError: boolean;
protected microsoftUserNotFoundError: boolean;

constructor(private activatedRoute: ActivatedRoute) {}

ngOnInit() {
this.activatedRoute.queryParams.subscribe((params) => {
this.googleUserNotFoundError = params['googleUserNotFound'] === 'true';
this.microsoftUserNotFoundError = params['microsoftUserNotFound'] === 'true';
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<mat-card
appearance="outlined"
class="standalone__content standalone__content--sm mat-elevation-z4 center"
>
<mat-card-content>
<h2 class="standalone__title accent" i18n>Create Account</h2>
<p class="mat-subtitle-2" i18n>Hi! This Microsoft user already has a WISE account.</p>
<mat-card-actions fxLayout="row" fxLayoutAlign="center center">
<button
class="button--social-login button--microsoft"
color="accent"
mat-flat-button
(click)="login()"
>
<img src="assets/img/icons/ms-logo.svg" i18n-alt alt="Microsoft logo" /><ng-container i18n
>Sign in with Microsoft</ng-container
>
</button>
</mat-card-actions>
</mat-card-content>
</mat-card>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RegisterMicrosoftUserAlreadyExistsComponent } from './register-microsoft-user-already-exists.component';
import { MatCardModule } from '@angular/material/card';

describe('RegisterMicrosoftUserAlreadyExistsComponent', () => {
let component: RegisterMicrosoftUserAlreadyExistsComponent;
let fixture: ComponentFixture<RegisterMicrosoftUserAlreadyExistsComponent>;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [RegisterMicrosoftUserAlreadyExistsComponent],
imports: [MatCardModule]
});
fixture = TestBed.createComponent(RegisterMicrosoftUserAlreadyExistsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Component } from '@angular/core';

@Component({
templateUrl: './register-microsoft-user-already-exists.component.html'
})
export class RegisterMicrosoftUserAlreadyExistsComponent {
protected login(): void {
window.location.href = `/api/microsoft-login?redirectUrl=/`;
}
}
4 changes: 3 additions & 1 deletion src/app/register/register-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { RegisterTeacherFormComponent } from './register-teacher-form/register-t
import { RegisterTeacherCompleteComponent } from './register-teacher-complete/register-teacher-complete.component';
import { RegisterStudentComponent } from './register-student/register-student.component';
import { RegisterGoogleUserAlreadyExistsComponent } from './register-google-user-already-exists/register-google-user-already-exists.component';
import { RegisterMicrosoftUserAlreadyExistsComponent } from './register-microsoft-user-already-exists/register-microsoft-user-already-exists.component';

const registerRoutes: Routes = [
{
Expand All @@ -23,7 +24,8 @@ const registerRoutes: Routes = [
{ path: 'teacher', component: RegisterTeacherComponent },
{ path: 'teacher/complete', component: RegisterTeacherCompleteComponent },
{ path: 'teacher/form', component: RegisterTeacherFormComponent },
{ path: 'googleUserAlreadyExists', component: RegisterGoogleUserAlreadyExistsComponent }
{ path: 'googleUserAlreadyExists', component: RegisterGoogleUserAlreadyExistsComponent },
{ path: 'microsoftUserAlreadyExists', component: RegisterMicrosoftUserAlreadyExistsComponent }
]
}
];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<mat-card class="standalone__content standalone__content--sm mat-elevation-z4 center">
<mat-card-content>
<h2 class="standalone__title accent" i18n>Your WISE account has been created!</h2>
<p *ngIf="!isUsingGoogleId" class="mat-subtitle-2" i18n>
<p *ngIf="!socialAccount" class="mat-subtitle-2" i18n>
Your username is: <span class="mat-headline-6">{{ username }}</span
>.
</p>
<p *ngIf="!isUsingGoogleId" i18n>
<p *ngIf="!socialAccount" i18n>
Please write this down. You will need it when signing in to WISE in the future.
</p>
<p>
<a *ngIf="!isUsingGoogleId" mat-flat-button color="primary" (click)="login()" i18n
<a *ngIf="!socialAccount" mat-flat-button color="primary" (click)="login()" i18n
>Sign In to Get Started</a
>
<a
Expand All @@ -23,6 +23,17 @@ <h2 class="standalone__title accent" i18n>Your WISE account has been created!</h
>Sign in with Google</ng-container
>
</a>
<a
*ngIf="isUsingMicrosoftId"
class="button--social-login button--microsoft"
href="{{ microsoftLogInURL }}"
color="accent"
mat-raised-button
>
<img src="assets/img/icons/ms-logo.svg" i18n-alt alt="Microsoft logo" /><ng-container i18n
>Sign in with Microsoft</ng-container
>
</a>
</p>
</mat-card-content>
</mat-card>
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ <h2 class="standalone__title accent" i18n>Create Student Account</h2>
>
</mat-form-field>
</p>
<div *ngIf="user.googleUserId == null">
<div *ngIf="user.googleUserId == null && user.microsoftUserId == null">
<p>
<mat-form-field appearance="fill" fxFlex>
<mat-label i18n>Security Question</mat-label>
Expand Down Expand Up @@ -130,7 +130,10 @@ <h2 class="standalone__title accent" i18n>Create Student Account</h2>
</mat-form-field>
</p>
</div>
<div *ngIf="user.googleUserId == null" formGroupName="passwords">
<div
*ngIf="user.googleUserId == null && user.microsoftUserId == null"
formGroupName="passwords"
>
<new-password-and-confirm
[formGroup]="passwordsFormGroup"
[passwordLabel]="passwordLabel"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ async function createAccount() {
await component.createAccount();
expect(routerNavigateSpy).toHaveBeenCalledWith([
'join/student/complete',
{ username: username, isUsingGoogleId: false }
{ username: username, isUsingGoogleId: false, isUsingMicrosoftId: false }
]);
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ export class RegisterStudentFormComponent extends RegisterUserFormComponent impl
ngOnInit() {
this.route.params.subscribe((params) => {
this.user.googleUserId = params['gID'];
if (!this.isUsingGoogleId()) {
this.user.microsoftUserId = params['mID'];
if (!this.isSocialAccount()) {
this.createStudentAccountFormGroup.addControl('passwords', this.passwordsFormGroup);
this.createStudentAccountFormGroup.addControl(
'securityQuestion',
Expand Down Expand Up @@ -99,10 +100,18 @@ export class RegisterStudentFormComponent extends RegisterUserFormComponent impl
this.changeDetectorRef.detectChanges();
}

private isSocialAccount(): boolean {
return this.isUsingGoogleId() || this.isUsingMicrosoftId();
}

isUsingGoogleId() {
return this.user.googleUserId != null;
}

private isUsingMicrosoftId(): boolean {
return this.user.microsoftUserId != null;
}

async createAccount() {
if (this.createStudentAccountFormGroup.valid) {
this.processing = true;
Expand All @@ -121,7 +130,11 @@ export class RegisterStudentFormComponent extends RegisterUserFormComponent impl
createAccountSuccess(response: any): void {
this.router.navigate([
'join/student/complete',
{ username: response.username, isUsingGoogleId: this.isUsingGoogleId() }
{
username: response.username,
isUsingGoogleId: this.isUsingGoogleId(),
isUsingMicrosoftId: this.isUsingMicrosoftId()
}
]);
this.processing = false;
}
Expand All @@ -138,10 +151,11 @@ export class RegisterStudentFormComponent extends RegisterUserFormComponent impl
const token = await this.recaptchaV3Service.execute('importantAction').toPromise();
this.user['token'] = token;
}
if (!this.isUsingGoogleId()) {
if (!this.isSocialAccount()) {
this.user['password'] = this.getPassword();
delete this.user['passwords'];
delete this.user['googleUserId'];
delete this.user['microsoftUserId'];
}
}

Expand Down
Loading

0 comments on commit 23f8208

Please sign in to comment.