diff --git a/package-lock.json b/package-lock.json
index 425937b..4fd7c0d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -29,7 +29,7 @@
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"@popperjs/core": "^2.11.6",
- "@types/youtube": "^0.0.50",
+ "angular-oauth2-oidc": "^17.0.1",
"aos": "^2.3.4",
"bootstrap": "^5.3.2",
"crypto-js": "^4.2.0",
@@ -6418,11 +6418,6 @@
"@types/node": "*"
}
},
- "node_modules/@types/youtube": {
- "version": "0.0.50",
- "resolved": "https://registry.npmjs.org/@types/youtube/-/youtube-0.0.50.tgz",
- "integrity": "sha512-d4GpH4uPYp9W07kc487tiq6V/EUHl18vZWFMbQoe4Sk9LXEWzFi/BMf9x7TI4m7/j7gU3KeX8H6M8aPBgykeLw=="
- },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
@@ -7139,6 +7134,18 @@
"ajv": "^8.8.2"
}
},
+ "node_modules/angular-oauth2-oidc": {
+ "version": "17.0.1",
+ "resolved": "https://registry.npmjs.org/angular-oauth2-oidc/-/angular-oauth2-oidc-17.0.1.tgz",
+ "integrity": "sha512-Yl4It9zFsYmoNS73sUvNJstbMW1x73ejKonzXLgU4XnSuBCt/0x8PnY5R3mHX4ZC/WmXBqQ/RfFwClrYW9Ywcg==",
+ "dependencies": {
+ "tslib": "^2.5.2"
+ },
+ "peerDependencies": {
+ "@angular/common": ">=14.0.0",
+ "@angular/core": ">=14.0.0"
+ }
+ },
"node_modules/ansi-colors": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
diff --git a/package.json b/package.json
index ab89666..3126a9b 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"@popperjs/core": "^2.11.6",
+ "angular-oauth2-oidc": "^17.0.1",
"aos": "^2.3.4",
"bootstrap": "^5.3.2",
"crypto-js": "^4.2.0",
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index a502b9d..e125390 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -8,6 +8,7 @@ import { SettingComponent } from './components/setting/setting.component';
import { authGuard } from './guards/auth/auth.guard';
import { HomeComponent } from './components/home/home.component';
import { NotFoundComponent } from './components/general/not-found/not-found.component';
+import { OauthButtonComponent } from './components/oauth/oauth-button/oauth-button.component';
const routes: Routes = [
{ path: '', component: HomeComponent, pathMatch: 'full' },
@@ -36,7 +37,6 @@ const routes: Routes = [
path: 'other',
loadChildren: () => import('./components/other-activity/other-activity.module').then(m => m.OtherActivityModule)
},
-
{ path: '404', component: NotFoundComponent },
{ path: '**', redirectTo: '/404', pathMatch: 'full' }
];
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index bfc5320..b510806 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
-import { HttpClientModule, HttpClient } from '@angular/common/http';
+import { HttpClientModule, HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
@@ -43,6 +43,8 @@ import { MatProgressBarModule } from '@angular/material/progress-bar';
import { QuestionEditDialogComponent } from './components/general/dialog/topic/question-edit-dialog/question-edit-dialog.component';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatChipsModule } from '@angular/material/chips';
+import { GithubCallbackComponent } from './components/callback/github-callback/github-callback.component';
+import { AuthInterceptor } from './guards/auth/interceptor/auth-interceptor.service';
// AOT compilation support
export function HttpLoaderFactory(http: HttpClient) {
@@ -76,7 +78,8 @@ export function HttpLoaderFactory(http: HttpClient) {
ConfettiComponent,
FileUploadComponent,
ConfirmDialogComponent,
- QuestionEditDialogComponent
+ QuestionEditDialogComponent,
+ GithubCallbackComponent
],
// ...
@@ -107,7 +110,13 @@ export function HttpLoaderFactory(http: HttpClient) {
MatProgressBarModule,
MatChipsModule // Add this line
],
- providers: [],
+ providers: [
+ {
+ provide: HTTP_INTERCEPTORS,
+ useClass: AuthInterceptor,
+ multi: true,
+ }
+ ],
bootstrap: [AppComponent]
})
export class AppModule { }
diff --git a/src/app/components/admin/admin-routing.module.ts b/src/app/components/admin/admin-routing.module.ts
index bdf5484..9c64866 100644
--- a/src/app/components/admin/admin-routing.module.ts
+++ b/src/app/components/admin/admin-routing.module.ts
@@ -9,6 +9,7 @@ import { ChatComponent } from './chat/chat.component';
import { MarkdownRendererComponent } from './markdown-renderer/markdown-renderer.component';
import { FileUploadComponent } from '../general/file/file-upload/file-upload.component';
import { CsharpInterviewQaComponent } from './topic/csharp-interview-qa/csharp-interview-qa.component';
+import { UserComponent } from './user/user/user.component';
const routes: Routes = [{
path: "", component: AdminComponent, children: [
@@ -19,7 +20,8 @@ const routes: Routes = [{
{ path: "chat", component: ChatComponent },
{ path: "markdown-renderer", component: MarkdownRendererComponent },
{ path: "file", component: FileUploadComponent },
- { path: "topic", component: CsharpInterviewQaComponent }
+ { path: "topic", component: CsharpInterviewQaComponent },
+ { path: "user", component: UserComponent }
]
}];
diff --git a/src/app/components/admin/admin.module.ts b/src/app/components/admin/admin.module.ts
index d16b4b3..c241bd7 100644
--- a/src/app/components/admin/admin.module.ts
+++ b/src/app/components/admin/admin.module.ts
@@ -80,7 +80,7 @@ import { CsharpInterviewQaComponent } from './topic/csharp-interview-qa/csharp-i
MatTableModule,
MatDialogModule,
MatSelectModule,
- MatOptionModule
+ MatOptionModule,
]
})
export class AdminModule { }
diff --git a/src/app/components/admin/user/user/user.component.html b/src/app/components/admin/user/user/user.component.html
index d039bb7..572de36 100644
--- a/src/app/components/admin/user/user/user.component.html
+++ b/src/app/components/admin/user/user/user.component.html
@@ -1 +1,235 @@
-
user works!
+
+
+
Your profile is 70% Complete
+
+
+
General Information
+
+
+
+
+
+
Change Password
+
+
+
+
Contact Information
+
+
+
+
Social Profiles
+
+
+
+
Send Email Notifications
+
+
+
+
+
diff --git a/src/app/components/admin/user/user/user.component.ts b/src/app/components/admin/user/user/user.component.ts
index 6f8bec9..01dd13c 100644
--- a/src/app/components/admin/user/user/user.component.ts
+++ b/src/app/components/admin/user/user/user.component.ts
@@ -6,5 +6,12 @@ import { Component } from '@angular/core';
styleUrls: ['./user.component.css']
})
export class UserComponent {
+handleKeyDown() {
+throw new Error('Method not implemented.');
+}
+uploadPicture() {
+throw new Error('Method not implemented.');
+}
+picture: any;
}
diff --git a/src/app/components/callback/github-callback/github-callback.component.css b/src/app/components/callback/github-callback/github-callback.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/components/callback/github-callback/github-callback.component.html b/src/app/components/callback/github-callback/github-callback.component.html
new file mode 100644
index 0000000..cbf5d37
--- /dev/null
+++ b/src/app/components/callback/github-callback/github-callback.component.html
@@ -0,0 +1 @@
+Processing login...
diff --git a/src/app/components/callback/github-callback/github-callback.component.spec.ts b/src/app/components/callback/github-callback/github-callback.component.spec.ts
new file mode 100644
index 0000000..fe317d0
--- /dev/null
+++ b/src/app/components/callback/github-callback/github-callback.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { GithubCallbackComponent } from './github-callback.component';
+
+describe('GithubCallbackComponent', () => {
+ let component: GithubCallbackComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [GithubCallbackComponent]
+ });
+ fixture = TestBed.createComponent(GithubCallbackComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/components/callback/github-callback/github-callback.component.ts b/src/app/components/callback/github-callback/github-callback.component.ts
new file mode 100644
index 0000000..5685ad9
--- /dev/null
+++ b/src/app/components/callback/github-callback/github-callback.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+import { AuthService } from 'src/app/guards/auth/auth.service';
+
+@Component({
+ selector: 'app-github-callback',
+ templateUrl: './github-callback.component.html',
+ styleUrls: ['./github-callback.component.css']
+})
+export class GithubCallbackComponent implements OnInit {
+ constructor(private authService: AuthService) { }
+
+ ngOnInit() {
+ this.authService.handleAuthentication();
+ }
+}
diff --git a/src/app/components/oauth/login/login.component.html b/src/app/components/oauth/login/login.component.html
index 3c43c6a..3b2e130 100644
--- a/src/app/components/oauth/login/login.component.html
+++ b/src/app/components/oauth/login/login.component.html
@@ -47,11 +47,21 @@ Sign in
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/app/components/oauth/login/login.component.ts b/src/app/components/oauth/login/login.component.ts
index 30c2114..5d83e4e 100644
--- a/src/app/components/oauth/login/login.component.ts
+++ b/src/app/components/oauth/login/login.component.ts
@@ -82,4 +82,19 @@ export class LoginComponent implements OnInit {
}
);
}
+
+ onLoginRequested(service: string): void {
+ console.log(`Login requested for: ${service}`);
+ switch (service.toLowerCase()) {
+ case 'github':
+ this.authService.initiateGithubLogin();
+ break;
+ case 'linkedin':
+ // Call LinkedIn login method
+ break;
+ // Add more cases for other services
+ default:
+ console.warn(`Login for service ${service} is not implemented.`);
+ }
+ }
}
diff --git a/src/app/components/oauth/oauth-button/oauth-button.component.html b/src/app/components/oauth/oauth-button/oauth-button.component.html
index 0c96ae8..7ff1d30 100644
--- a/src/app/components/oauth/oauth-button/oauth-button.component.html
+++ b/src/app/components/oauth/oauth-button/oauth-button.component.html
@@ -1,3 +1,3 @@
-
+
{{ service }}
diff --git a/src/app/components/oauth/oauth-button/oauth-button.component.ts b/src/app/components/oauth/oauth-button/oauth-button.component.ts
index e3ea7cd..cc88b95 100644
--- a/src/app/components/oauth/oauth-button/oauth-button.component.ts
+++ b/src/app/components/oauth/oauth-button/oauth-button.component.ts
@@ -13,12 +13,14 @@ import { pulse } from 'ng-animate';
]
})
export class OauthButtonComponent {
+
@Input() service = '';
@Input() iconClass = '';
@HostBinding('@pulse') pulse = true;
@Output() loginRequested = new EventEmitter();
- public login(): void {
+ public login(event: Event): void {
+ event.preventDefault(); // Prevent default anchor behavior
console.log('Login requested for ' + this.service);
this.loginRequested.emit(this.service);
}
diff --git a/src/app/components/oauth/oauth-routing.module.ts b/src/app/components/oauth/oauth-routing.module.ts
index 8f0e304..30b592c 100644
--- a/src/app/components/oauth/oauth-routing.module.ts
+++ b/src/app/components/oauth/oauth-routing.module.ts
@@ -7,6 +7,7 @@ import { LoginComponent } from './login/login.component';
import { SignupComponent } from './signup/signup.component';
import { LoginSuccessComponent } from './login-success/login-success.component';
import { LoginFailureComponent } from './login-failure/login-failure.component';
+import { GithubCallbackComponent } from '../callback/github-callback/github-callback.component';
const routes: Routes = [
{
@@ -19,6 +20,7 @@ const routes: Routes = [
{ path: "signup", component: SignupComponent },
{ path: 'login-success', component: LoginSuccessComponent },
{ path: 'login-failure', component: LoginFailureComponent },
+ { path: 'github-callback', component: GithubCallbackComponent },
]
}];
diff --git a/src/app/guards/auth/auth.service.ts b/src/app/guards/auth/auth.service.ts
index c5925f1..d1a960c 100644
--- a/src/app/guards/auth/auth.service.ts
+++ b/src/app/guards/auth/auth.service.ts
@@ -2,24 +2,49 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { UserDetails } from '../../models/user-details';
import { ProfileService } from '../../services/profile/profile.service';
+import { HttpClient } from '@angular/common/http';
+import { environment } from 'src/environments/environment';
+import { OAuthService, AuthConfig, OAuthSuccessEvent } from 'angular-oauth2-oidc';
+import { ActivatedRoute, Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class AuthService {
+ // private isAuthenticated = new BehaviorSubject(false);
+ private authState = new BehaviorSubject(this.hasValidToken());
+ private readonly tokenStorageKey = 'access_token';
- private isAuthenticated = new BehaviorSubject(false);
- constructor(private profileService: ProfileService) { }
+ constructor(private profileService: ProfileService, private http: HttpClient,
+ private router: Router,
+ private route: ActivatedRoute) {
+ }
+ isAuthenticated() {
+ return this.authState.asObservable();
+ }
+ saveAccessToken(token: string) {
+ localStorage.setItem(this.tokenStorageKey, token);
+ this.authState.next(true);
+ }
+ getAccessToken() {
+ return localStorage.getItem(this.tokenStorageKey);
+ }
+ hasValidToken(): boolean {
+ const token = this.getAccessToken();
+ // Here you would check if the token is valid, e.g., not expired
+ // For simplicity, we assume a token exists means it's valid
+ return !!token;
+ }
- login(organizationId: string, username: string, password: string): boolean {
+ public login(organizationId: string, username: string, password: string): boolean {
// Here you should implement your authentication logic, e.g., check credentials against a backend
// For this example, we're just setting isAuthenticated to true
- this.isAuthenticated.next(true);
+ this.authState.next(true);
console.log('user authenticated successfully!');
console.log(password);
// Assuming getUserDetails returns an Observable, you need to subscribe to it to get the data
- this.profileService.get(organizationId,username).subscribe((userDetails: UserDetails) => {
+ this.profileService.get(organizationId, username).subscribe((userDetails: UserDetails) => {
// Store user data after retrieving it from the profile service
this.storeUserData('some-token', userDetails); // Replace 'some-token' with the actual token
});
@@ -30,8 +55,10 @@ export class AuthService {
logout(): void {
- this.isAuthenticated.next(false);
+ // this.isAuthenticated.next(false);
+ this.authState.next(false);
+ localStorage.removeItem(this.tokenStorageKey);
localStorage.removeItem('token');
localStorage.removeItem('currentUserName');
localStorage.removeItem('organizationId');
@@ -40,7 +67,8 @@ export class AuthService {
}
isLoggedIn(): Observable {
- return this.isAuthenticated.asObservable();
+ return this.isAuthenticated();
+ // return this.isAuthenticated.asObservable();
}
storeUserData(token: string, user: UserDetails) {
@@ -65,4 +93,52 @@ export class AuthService {
const oauthUrl = `/auth/${provider.toLowerCase()}`;
window.location.href = oauthUrl;
}
+
+ public handleAuthentication(): void {
+ // Get the code from the URL query parameters
+ this.route.queryParams.subscribe(params => {
+ const code = params['code'];
+
+ if (code) {
+ this.sendCodeToBackend(code);
+ } else {
+ // Handle the case where there is no code in the query parameters
+ }
+ });
+ }
+ private sendCodeToBackend(code: string): void {
+ const apiUrl = `${environment.awsUserApiBaseUrl}`;
+ this.http.post(`${apiUrl}/github/callback`, { code }).subscribe(
+ response => {
+ // The backend should return an object containing the access token or the session info
+ // Save the session info as needed
+ // Redirect or perform actions as needed after successful login
+ console.log('response from github callback:');
+ console.log(response);
+
+ this.router.navigate(['/index']); // Redirect to dashboard or other component
+ },
+ err => {
+ console.error('Error exchanging code for token:', err);
+ // Handle the error
+ }
+ );
+ }
+
+ public initiateGithubLogin(): void {
+ const clientId = environment.github.clientId;
+ const redirectUri = environment.github.redirectUri;
+ const scope = 'read:user'; // Adjust the scope according to your needs
+ const state = this.generateRandomString(); // A random string to prevent CSRF attacks
+
+ const githubAuthUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}`;
+
+ window.location.href = githubAuthUrl;
+ }
+ private generateRandomString(): string {
+ const array = new Uint32Array(10);
+ window.crypto.getRandomValues(array);
+ return array.join('');
+ }
+
}
diff --git a/src/app/guards/auth/interceptor/auth-interceptor.service.spec.ts b/src/app/guards/auth/interceptor/auth-interceptor.service.spec.ts
new file mode 100644
index 0000000..03e16c3
--- /dev/null
+++ b/src/app/guards/auth/interceptor/auth-interceptor.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { AuthInterceptorService } from './auth-interceptor.service';
+
+describe('AuthInterceptorService', () => {
+ let service: AuthInterceptorService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(AuthInterceptorService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src/app/guards/auth/interceptor/auth-interceptor.service.ts b/src/app/guards/auth/interceptor/auth-interceptor.service.ts
new file mode 100644
index 0000000..a34d7c5
--- /dev/null
+++ b/src/app/guards/auth/interceptor/auth-interceptor.service.ts
@@ -0,0 +1,31 @@
+import { Injectable } from '@angular/core';
+import {
+ HttpInterceptor,
+ HttpRequest,
+ HttpHandler,
+ HttpEvent,
+} from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { AuthService } from '../auth.service';
+
+@Injectable()
+export class AuthInterceptor implements HttpInterceptor {
+ constructor(private authService: AuthService) { }
+
+ intercept(
+ req: HttpRequest,
+ next: HttpHandler
+ ): Observable> {
+ const accessToken = this.authService.getAccessToken();
+ if (accessToken) {
+ const clonedReq = req.clone({
+ setHeaders: {
+ Authorization: `Bearer ${accessToken}`,
+ },
+ });
+ return next.handle(clonedReq);
+ } else {
+ return next.handle(req);
+ }
+ }
+}
diff --git a/src/app/models/admin/navbar/menu.ts b/src/app/models/admin/navbar/menu.ts
index 2d08470..4e06c05 100644
--- a/src/app/models/admin/navbar/menu.ts
+++ b/src/app/models/admin/navbar/menu.ts
@@ -10,25 +10,25 @@ export const menu: NavItem[] = [
displayName: 'User',
iconName: 'face',
route: 'user',
- children: [
- {
- displayName: 'Account Info',
- iconName: 'account_box',
- route: 'user/account-info'
- }
- ]
+ // children: [
+ // {
+ // displayName: 'Account Info',
+ // iconName: 'account_box',
+ // route: 'user/account-info'
+ // }
+ // ]
},
{
displayName: 'Organization',
iconName: 'business',
route: 'organization',
- children: [
- {
- displayName: 'Account Info',
- iconName: 'account_box',
- route: '/admin/organization'
- }
- ]
+ // children: [
+ // {
+ // displayName: 'Account Info',
+ // iconName: 'account_box',
+ // route: '/admin/organization'
+ // }
+ // ]
},
{
displayName: 'Visitor',
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index a0a5fa2..b184b28 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -68,5 +68,9 @@ export const environment = {
fileApiEndpoints: {
getUrl:'/api/file/geturl/{key}',
generateUrl:'/api/file/generateUrl/{key}',
+ },
+ github:{
+ clientId: '26cb4ea080bd30fe7461',
+ redirectUri: 'http://localhost:4200/authentication/github-callback',
}
};