diff --git a/src/Client/src/app/app.html b/src/Client/src/app/app.html
index c142ed6..70d5048 100644
--- a/src/Client/src/app/app.html
+++ b/src/Client/src/app/app.html
@@ -9,10 +9,10 @@
diff --git a/src/Client/src/app/app.routes.ts b/src/Client/src/app/app.routes.ts
index 9a9b176..1a884a2 100644
--- a/src/Client/src/app/app.routes.ts
+++ b/src/Client/src/app/app.routes.ts
@@ -1,12 +1,5 @@
import { Routes } from '@angular/router';
-import { ForgotPasswordComponent } from './pages/account/forgotpassword/forgotpassword.component';
-import { LoginComponent } from './pages/account/login/login.component';
-import { RegisterComponent } from './pages/account/register/register.component';
import { AboutComponent } from './pages/legal/about/about.component';
-import { SettingsComponent } from './pages/account/settings/settings.component';
-import { LogoutComponent } from './pages/account/logout/logout.component';
-import { ResetPasswordComponent } from './pages/account/resetpassword/resetpassword.component';
-import { VerifyEmailComponent } from './pages/account/verifyemail/verifyemail.component';
import { HomeComponent } from './pages/main/home/home.component';
import { ContactComponent } from './pages/legal/contact/contact.component';
import { PrivacyComponent } from './pages/legal/privacy/privacy.component';
@@ -30,15 +23,6 @@ export const routes: Routes = [
// Company
{ path: "company/connect", component: CompanyConnectComponent },
- // Account stuff
- { path: "account/forgotpassword", component: ForgotPasswordComponent },
- { path: "account/resetpassword", component: ResetPasswordComponent },
- { path: "account/verifyemail", component: VerifyEmailComponent },
- { path: "account/login", component: LoginComponent },
- { path: "account/logout", component: LogoutComponent },
- { path: "account/register", component: RegisterComponent },
- { path: "account/settings", component: SettingsComponent },
-
// Legal
{ path: "about", component: AboutComponent },
{ path: "contact", component: ContactComponent },
diff --git a/src/Client/src/app/app.ts b/src/Client/src/app/app.ts
index 596a131..bcba189 100644
--- a/src/Client/src/app/app.ts
+++ b/src/Client/src/app/app.ts
@@ -1,7 +1,8 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
-import { Router, RouterModule, RouterOutlet } from '@angular/router';
+import { Router, RouterModule, RouterOutlet, ActivatedRoute } from '@angular/router';
import { Authentication } from './services/Authentication';
-import { CommonModule } from '@angular/common';
+import { CommonModule, Location } from '@angular/common';
+import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-root',
@@ -15,7 +16,31 @@ export class App {
@ViewChild('jobsLink') jobLink!: ElementRef;
@ViewChild('resumesLink') resumeLink!: ElementRef;
- constructor(public auth: Authentication, private router: Router){}
+ constructor( private http: HttpClient, public auth: Authentication, private router: Router, private route: ActivatedRoute, private location: Location){
+ this.route.queryParams.subscribe(params => {
+
+ const loginToken = params['LoginToken'];
+ console.log("LoginToken : " + loginToken);
+
+ if (loginToken){
+ this.http.post( "api/account/loginticket", JSON.stringify(loginToken), { headers: {'Content-Type': 'application/json'} } ).subscribe({
+ next: data => {
+ auth.getLoginState();
+ const pathWithoutQuery = this.location.path().split('?')[0];
+ this.location.replaceState(pathWithoutQuery);
+ },
+ error: err => {
+ auth.getLoginState();
+ const pathWithoutQuery = this.location.path().split('?')[0];
+ this.location.replaceState(pathWithoutQuery);
+ }
+ })
+ }else{
+ auth.getLoginState();
+ }
+
+ });
+ }
ngAfterViewInit(){
let ViewLinks = [ this.homeLink, this.resumeLink, this.jobLink ];
diff --git a/src/Client/src/app/models/Account.ts b/src/Client/src/app/models/Account.ts
index 99c9acf..12f75e0 100644
--- a/src/Client/src/app/models/Account.ts
+++ b/src/Client/src/app/models/Account.ts
@@ -1,5 +1,5 @@
export class Account {
- public id: number = 0;
+ public id: number = -1;
public userName: string = "";
public email: string = "";
public emailVerified: boolean = false;
diff --git a/src/Client/src/app/pages/account/forgotpassword/forgotpassword.component.html b/src/Client/src/app/pages/account/forgotpassword/forgotpassword.component.html
deleted file mode 100644
index c0e6404..0000000
--- a/src/Client/src/app/pages/account/forgotpassword/forgotpassword.component.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/forgotpassword/forgotpassword.component.ts b/src/Client/src/app/pages/account/forgotpassword/forgotpassword.component.ts
deleted file mode 100644
index 1a546a8..0000000
--- a/src/Client/src/app/pages/account/forgotpassword/forgotpassword.component.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Component } from '@angular/core';
-import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
-import { FormsModule } from '@angular/forms';
-import { Router, ActivatedRoute } from '@angular/router';
-import { Title } from '@angular/platform-browser';
-import { CommonModule } from '@angular/common';
-
-@Component({
- selector: 'account-forgot',
- templateUrl: './forgotpassword.component.html',
- imports: [ FormsModule, CommonModule ]
-})
-export class ForgotPasswordComponent {
- email: string = "";
- errorMsgs: string[] = [];
- returnURL: string = '/';
-
- constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title) {
- this.title.setTitle("Forgot Password | Mistox");
- this.route.queryParams.subscribe(params => {
- this.returnURL = params['returnURL'] || '/';
- });
- }
-
- sleep(ms: number) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
-
- onSubmit() {
- // Clear errors
- this.errorMsgs = [];
-
- // Send to server and wait for response
- this.errorMsgs.push("Waiting for response from server");
- const body = new HttpParams()
- .set("Email", this.email)
- const headers = new HttpHeaders({
- 'Content-Type': 'application/x-www-form-urlencoded',
- });
- this.http.post( "api/account/sendresetpassword", body, { headers, responseType: "text" } ).subscribe({
- next: async (data) => {
- if (data.trim() == "Success"){
- this.errorMsgs = ["Reset-password sent"];
- await this.sleep(3000);
- this.router.navigate([this.returnURL]);
- }else{
- this.errorMsgs = [data];
- }
- },
- error: err => {
- console.log("HTTP Error Signing In: ", err);
- }
- });
- }
-}
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/login/login.component.html b/src/Client/src/app/pages/account/login/login.component.html
deleted file mode 100644
index 52431bb..0000000
--- a/src/Client/src/app/pages/account/login/login.component.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/login/login.component.ts b/src/Client/src/app/pages/account/login/login.component.ts
deleted file mode 100644
index 751f5ed..0000000
--- a/src/Client/src/app/pages/account/login/login.component.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Component } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { FormsModule } from '@angular/forms';
-import { Router, ActivatedRoute } from '@angular/router';
-import { Title } from '@angular/platform-browser';
-import { CommonModule } from '@angular/common';
-import { Authentication, SessionType } from '../../../services/Authentication';
-
-@Component({
- selector: 'account-login',
- templateUrl: './login.component.html',
- imports: [ FormsModule, CommonModule ],
- standalone: true
-})
-export class LoginComponent {
- UserName: string = "";
- Password: string = "";
- StayLoggedIn: boolean = false;
- errorMsgs: string[] = [];
- returnURL: string = '/';
-
- constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
- this.title.setTitle("Login | Mistox");
- this.route.queryParams.subscribe(params => {
- this.returnURL = params['returnURL'] || '/';
- });
- }
-
- onSubmit() {
- this.errorMsgs = [];
-
- if (!this.UserName) {
- this.errorMsgs.push("The 'username' field is required");
- }
- if (!this.Password) {
- this.errorMsgs.push("The 'password' field is required");
- }
- if (this.Password.length < 6) {
- this.errorMsgs.push("Password must be at least 6 Characters long");
- }
- if (this.errorMsgs.length > 0) {
- return;
- }
-
- this.errorMsgs.push("Waiting for response from server");
- this.auth.Login(this.UserName, this.Password, this.StayLoggedIn).subscribe({
- next: data => {
- this.router.navigate([this.returnURL]);
- },
- error: err => {
- this.errorMsgs = [ err.error ];
- }
- })
- }
-}
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/logout/logout.component.html b/src/Client/src/app/pages/account/logout/logout.component.html
deleted file mode 100644
index e69de29..0000000
diff --git a/src/Client/src/app/pages/account/logout/logout.component.ts b/src/Client/src/app/pages/account/logout/logout.component.ts
deleted file mode 100644
index 130ee39..0000000
--- a/src/Client/src/app/pages/account/logout/logout.component.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Component } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { FormsModule } from '@angular/forms';
-import { Router, ActivatedRoute } from '@angular/router';
-import { Title } from '@angular/platform-browser';
-import { CommonModule } from '@angular/common';
-import { Authentication } from '../../../services/Authentication';
-
-@Component({
- selector: 'account-logout',
- templateUrl: './logout.component.html',
- imports: [ FormsModule, CommonModule ],
- standalone: true
-})
-export class LogoutComponent {
- errorMsgs: string[] = [];
- returnURL: string = '/';
-
- constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
- this.title.setTitle("Logout | Mistox");
- }
-
- ngAfterViewInit(){
- this.auth.Logout().subscribe({
- next: data => {
- window.location.href = "";
- }
- });
- }
-}
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/register/register.component.html b/src/Client/src/app/pages/account/register/register.component.html
deleted file mode 100644
index e324cff..0000000
--- a/src/Client/src/app/pages/account/register/register.component.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/register/register.component.ts b/src/Client/src/app/pages/account/register/register.component.ts
deleted file mode 100644
index 321c552..0000000
--- a/src/Client/src/app/pages/account/register/register.component.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { Component } from '@angular/core';
-import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
-import { FormsModule } from '@angular/forms';
-import { Router, ActivatedRoute } from '@angular/router';
-import { Account } from '../../../models/Account';
-import { Title } from '@angular/platform-browser';
-import { CommonModule } from '@angular/common';
-
-@Component({
- selector: 'account-register',
- templateUrl: './register.component.html',
- imports: [ FormsModule, CommonModule ]
-})
-export class RegisterComponent {
- userName: string = ""
- email: string = "";
- passwordHash: string = "";
- passwordHash2: string = "";
- error: string = "";
-
- errorMsgs: string[] = [];
- returnURL: string = '/';
-
- constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
- this.title.setTitle("Register | Mistox");
- this.route.queryParams.subscribe(params => {
- this.returnURL = params['returnURL'] || '/';
- });
- }
-
- sleep(ms: number) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
-
- onSubmit() {
- // Clear errors
- this.errorMsgs = [];
-
- // Validate data
- const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
- if (!regex.test(this.email)){
- this.errorMsgs.push("A valid email is required");
- }
- if (!this.userName) {
- this.errorMsgs.push("The 'username' field is required");
- }
- if (!this.passwordHash) {
- this.errorMsgs.push("The 'password' field is required");
- }
- if (this.passwordHash.length < 6) {
- this.errorMsgs.push("Password must be at least 6 Characters long");
- }
- if (this.passwordHash !== this.passwordHash2){
- this.errorMsgs.push("Passwords don't match");
- }
- if (this.errorMsgs.length > 0) {
- return;
- }
-
- // Send to server and wait for response
- this.errorMsgs.push("Waiting for response from server");
- const body = new HttpParams()
- .set("Email", this.email)
- .set("UserName", this.userName)
- .set("PasswordHash", this.passwordHash);
- const headers = new HttpHeaders({
- 'Content-Type': 'application/x-www-form-urlencoded'
- });
- this.http.post( "api/account/register", body, { headers } ).subscribe({
- next: async (data) => {
- this.errorMsgs = ["Account Created"];
- await this.sleep(3000);
- this.router.navigate([this.returnURL]);
- },
- error: err => {
- this.errorMsgs = [ err.error ]
- }
- });
- }
-}
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/resetpassword/resetpassword.component.html b/src/Client/src/app/pages/account/resetpassword/resetpassword.component.html
deleted file mode 100644
index 3c3a393..0000000
--- a/src/Client/src/app/pages/account/resetpassword/resetpassword.component.html
+++ /dev/null
@@ -1,29 +0,0 @@
-
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/resetpassword/resetpassword.component.ts b/src/Client/src/app/pages/account/resetpassword/resetpassword.component.ts
deleted file mode 100644
index 7cb2467..0000000
--- a/src/Client/src/app/pages/account/resetpassword/resetpassword.component.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { Component } from '@angular/core';
-import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
-import { FormsModule } from '@angular/forms';
-import { Router, ActivatedRoute } from '@angular/router';
-import { Title } from '@angular/platform-browser';
-import { CommonModule } from '@angular/common';
-
-@Component({
- selector: 'account-reset',
- templateUrl: './resetpassword.component.html',
- imports: [ FormsModule, CommonModule ]
-})
-export class ResetPasswordComponent {
-
- UserName: string = "";
- ResetPwd: string = "";
- Password: string = "";
- PassworR: string = "";
-
- errorMsgs: string[] = [];
-
- constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
- this.title.setTitle("Reset Password | Mistox");
- this.route.queryParams.subscribe(params => {
- this.UserName = params['UserName'] || '';
- this.ResetPwd = params['ResetPwd'] || '';
- });
- }
-
- sleep(ms: number) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
-
- onSubmit() {
- if (this.Password != this.PassworR){
- this.errorMsgs.push("Passwords must match");
- }
- if (this.Password.length < 6){
- this.errorMsgs.push("Password must be at least 6 Characters long");
- }
- if (this.errorMsgs.length == 0){
- // Send to server and wait for response
- this.errorMsgs.push("Waiting for response from server");
- const body = new HttpParams()
- .set("UserName", this.UserName)
- .set("NewPassword", this.Password)
- .set("ResetToken", this.ResetPwd);
- const headers = new HttpHeaders({
- 'Content-Type': 'application/x-www-form-urlencoded'
- });
- this.http.post( "api/account/resetpassword", body, { headers } ).subscribe({
- next: async (data) => {
- if (data == true){
- this.errorMsgs = ["Password reset successfully"];
- await this.sleep(3000);
- this.router.navigate(["/account/login"]);
- }else{
- this.errorMsgs = ["An error has ocurred"];
- await this.sleep(3000);
- this.router.navigate(["/account/sendresetpassword"]);
- }
- },
- error: err => {
- console.log("HTTP Error Signing In: ", err);
- }
- });
- }
- }
-}
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/settings/settings.component.html b/src/Client/src/app/pages/account/settings/settings.component.html
deleted file mode 100644
index e69de29..0000000
diff --git a/src/Client/src/app/pages/account/settings/settings.component.ts b/src/Client/src/app/pages/account/settings/settings.component.ts
deleted file mode 100644
index de3b951..0000000
--- a/src/Client/src/app/pages/account/settings/settings.component.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Component } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { FormsModule } from '@angular/forms';
-import { Router, ActivatedRoute } from '@angular/router';
-import { Account } from '../../../models/Account';
-import { Title } from '@angular/platform-browser';
-import { CommonModule } from '@angular/common';
-
-@Component({
- selector: 'account-settings',
- templateUrl: './settings.component.html',
- imports: [ FormsModule, CommonModule ]
-})
-export class SettingsComponent {
- user!: Account;
- errorMsgs: string[] = [];
- returnURL: string = '/';
-
- constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
- this.title.setTitle("Settings | Mistox");
- this.route.queryParams.subscribe(params => {
- this.returnURL = params['returnURL'] || '/';
- });
- }
-
- onSubmit() {
-
- }
-}
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/verifyemail/verifyemail.component.html b/src/Client/src/app/pages/account/verifyemail/verifyemail.component.html
deleted file mode 100644
index a8a8ddc..0000000
--- a/src/Client/src/app/pages/account/verifyemail/verifyemail.component.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/src/Client/src/app/pages/account/verifyemail/verifyemail.component.ts b/src/Client/src/app/pages/account/verifyemail/verifyemail.component.ts
deleted file mode 100644
index 044a57c..0000000
--- a/src/Client/src/app/pages/account/verifyemail/verifyemail.component.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Component } from '@angular/core';
-import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
-import { FormsModule } from '@angular/forms';
-import { Router, ActivatedRoute } from '@angular/router';
-import { Title } from '@angular/platform-browser';
-import { CommonModule } from '@angular/common';
-
-@Component({
- selector: 'account-verifyemail',
- templateUrl: './verifyemail.component.html',
- imports: [ FormsModule, CommonModule ]
-})
-export class VerifyEmailComponent {
-
- UserName: string = "";
- Guid: string = "";
- Result: string = "";
-
- constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
- this.title.setTitle("Verify Email | Mistox");
- this.route.queryParams.subscribe(params => {
- this.UserName = params['UserName'] || '';
- this.Guid = params['Guid'] || '';
- });
- }
-
- sleep(ms: number) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
-
- async onSubmit() {
- // Send to server and wait for response
- const body = new HttpParams()
- .set("UserName", this.UserName)
- .set("EmailToken", this.Guid);
- const headers = new HttpHeaders({
- 'Content-Type': 'application/x-www-form-urlencoded'
- });
- this.http.post( "api/account/verifyemail", body, { headers } ).subscribe({
- next: async (data) => {
- if (data == true){
- this.Result = "Verified Email Successfully";
- }else{
- this.Result = "Email was not able to be verified please resend email";
- }
- await this.sleep(3000);
- this.router.navigate(["/"]);
- },
- error: err => {
- console.log("HTTP Error Signing In: ", err);
- }
- });
- }
-}
\ No newline at end of file
diff --git a/src/Client/src/app/services/Authentication.ts b/src/Client/src/app/services/Authentication.ts
index 60803ba..f653b1b 100644
--- a/src/Client/src/app/services/Authentication.ts
+++ b/src/Client/src/app/services/Authentication.ts
@@ -6,30 +6,20 @@ import { HttpClient, HttpHeaders, HttpParams } from "@angular/common/http";
@Injectable({ providedIn: 'root' })
export class Authentication{
- private _user = new BehaviorSubject(this.getUserFromStorage());
+ private _user = new BehaviorSubject( new Account );
user$ = this._user.asObservable();
constructor( private http: HttpClient){ }
- Login(UserName: string, Password: string, StayLoggedIn: boolean): Observable {
-
- const body = new HttpParams()
- .set("UserName", UserName)
- .set("PasswordHash", Password)
- .set("StayLoggedIn", StayLoggedIn );
- const headers = new HttpHeaders({
- 'Content-Type': 'application/x-www-form-urlencoded'
- });
-
- let sub = this.http.post( "api/account/login", body, { headers } );
+ getLoginState(): Observable {
+ let sub = this.http.post( "api/account/loginState", {}, {} );
sub.subscribe({
next: data => {
data.passwordHash = "";
this._user.next(data);
- this.setUserToStorage(data, StayLoggedIn == true ? SessionType.Forever : SessionType.Session);
},
error: err => {
- console.log("HTTP Error Signing In: ", err.error);
+ console.log("No login state found: ", err.error);
}
});
return sub;
@@ -37,7 +27,6 @@ export class Authentication{
Logout(){
this._user.next( new Account );
- this.delUserFromStorage();
return this.http.post( "api/account/logout", {}, { responseType: 'json' } );
}
@@ -48,36 +37,4 @@ export class Authentication{
get loggedInUser(): Account {
return this._user.value;
}
-
- private getUserFromStorage(): Account {
- const foreverUser = localStorage.getItem('user');
- const sessionUser = sessionStorage.getItem('user');
- let user = null;
- if (foreverUser != null){
- user = JSON.parse(foreverUser)
- } else if (sessionUser != null){
- user = JSON.parse(sessionUser)
- } else {
- user = new Account();
- user.id = -1;
- }
- return user;
- }
- private setUserToStorage(user: Account, session: SessionType): void {
- if (session == SessionType.Forever){
- localStorage.setItem('user', JSON.stringify(user));
- }else if(session == SessionType.Session){
- sessionStorage.setItem('user', JSON.stringify(user));
- }
- }
- private delUserFromStorage(): void {
- localStorage.removeItem('user');
- sessionStorage.removeItem('user');
- }
-
-}
-
-export enum SessionType {
- Forever,
- Session
}
\ No newline at end of file
diff --git a/src/Server/Controllers/AuthenticationController.cs b/src/Server/Controllers/AuthenticationController.cs
index 81fcf06..2a55b40 100755
--- a/src/Server/Controllers/AuthenticationController.cs
+++ b/src/Server/Controllers/AuthenticationController.cs
@@ -1,279 +1,51 @@
using Microsoft.AspNetCore.Mvc;
-using BoredCareers.Services;
using BoredCareers.Services.DatabaseService;
using BoredCareers.Entities;
using System.Web.Http;
+using System.Text.Json;
+using System.Text;
namespace BoredCareers.Controllers {
[ApiController]
[Route("api/account/")]
public class AuthenticationController : MistoxControllerBase {
- EmailService _emailContext;
+ public AuthenticationController(DatabaseService db) : base(db) { }
- public AuthenticationController(DatabaseService db, EmailService emailContext) : base(db) {
- _emailContext = emailContext;
- }
-
- [Route("login")]
- [HttpPost]
- public async Task> Login([FromForm] string UserName, [FromForm] string PasswordHash, [FromForm] bool StayLoggedIn) {
- try {
- Account? test = await _databaseService.GetAccount(UserName.ToLower());
- if (test != null) {
- if (test.EmailVerified == true) {
- if (test.FailedPasswordLock) {
- if (test.CurrentPasswordAttempts >= test.PasswordAttempts) {
- return NotFound("Too many failed password attempts. Please reset your password");
- }
- }
- if (BCrypt.Net.BCrypt.Verify(PasswordHash, test.PasswordHash)) {
- test.CurrentPasswordAttempts = 0;
- await _databaseService.SetAccount(test);
-
- string jwt = BoredCareersJWT.GenereateJWTToken(test.ID, StayLoggedIn);
- BoredCareersJWT.SignIn(Response, StayLoggedIn, jwt);
-
- return Ok(test);
- } else {
- test.CurrentPasswordAttempts += 1;
- await _databaseService.SetAccount(test);
- return NotFound("Wrong Password");
- }
- } else {
- await SendVerify(test.UserName);
- return NotFound("A new verify email has been sent. \n Note only 1 email send every 5 mintes");
- }
- }
- return NotFound("Account Not Found");
- } catch (Exception ex) {
- Console.WriteLine("Login Error: " + ex.Message);
- return NotFound("An internal server error has occured");
+ [HttpPost("loginState")]
+ public ActionResult LoginState() {
+ if (isLoggedIn()) {
+ return Ok(getLoggedInUser());
}
+ return NotFound("Not logged in");
}
- [Route("register")]
- [HttpPost]
- public async Task> Register([FromForm] string Email, [FromForm] string UserName, [FromForm] string PasswordHash) {
- try {
- if (await _databaseService.GetAccount(UserName.ToLower()) == null) {
- if (await _databaseService.GetAccount(Email.ToLower()) == null) {
- Account created = new Account() {
- UserName = UserName.ToLower(),
- Email = Email.ToLower(),
- EmailVerified = false,
- PasswordHash = BCrypt.Net.BCrypt.HashPassword(PasswordHash),
- };
- await _databaseService.SetAccount(created);
- Account? loadedAccount = await _databaseService.GetAccount(Email.ToLower());
- if (loadedAccount != null) {
- await SendVerify(loadedAccount.UserName);
- return Ok(loadedAccount);
- }
- return NotFound("Unable to create the account");
- } else {
- return NotFound("Email is already in use");
- }
- } else {
- return NotFound("UserName is taken");
- }
- } catch (Exception ex) {
- Console.WriteLine("Register Error: " + ex.Message);
- return NotFound("An internal server error has occured");
- }
-
- }
-
- [Route("changepassword")]
- [HttpPost]
- public async Task ChangePassword([FromForm] string OldPassword, [FromForm] string NewPassword) {
- try {
- if (isLoggedIn()) {
- Account user = await getLoggedInUser();
- if (BCrypt.Net.BCrypt.Verify(OldPassword, user.PasswordHash)) {
- user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword);
- user.CurrentPasswordAttempts = 0;
- await _databaseService.SetAccount(user);
- return Ok();
- }
- }
- return NotFound("Not logged in");
- } catch (Exception ex) {
- Console.WriteLine("ChangePassword Error: " + ex.Message);
- return NotFound("An internal server error has occured");
- }
- }
-
- [Route("toggleaccountlock")]
- [HttpPost]
- public async Task> ToggleAccountLock([FromForm] bool AccountLock) {
- try {
- if (isLoggedIn()) {
- Account user = await getLoggedInUser();
- user.FailedPasswordLock = AccountLock;
- user.CurrentPasswordAttempts = 0;
- await _databaseService.SetAccount(user);
+ [HttpPost("loginticket")]
+ public async Task LoginTicket([FromBody] string LoginToken) {
+ using (HttpClient client = new HttpClient()) {
+ var payload = new { ticket = LoginToken };
+ StringContent jsonPayload = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
+ HttpResponseMessage JWTResponse = await client.PostAsync("https://auth.mistox.com/api/auth/token", jsonPayload);
+ if (JWTResponse.IsSuccessStatusCode) {
+ string JWT = await JWTResponse.Content.ReadAsStringAsync();
+ signIn(JWT);
return Ok();
+ } else {
+ string error = await JWTResponse.Content.ReadAsStringAsync();
+ return NotFound(error);
}
- return NotFound("Not logged in");
- } catch (Exception ex) {
- Console.WriteLine("ToggleAccountLock Error: " + ex.Message);
- return NotFound("An internal server error has occured");
}
}
- [Route("get")]
- [HttpPost]
- public async Task> Get() {
- try {
- if (isLoggedIn()) {
- return Ok(await getLoggedInUser());
- }
- return NotFound("Not logged in");
- } catch (Exception ex) {
- Console.WriteLine("Get Error: " + ex);
- return NotFound("An internal server error has occured");
- }
- }
-
- [Route("logout")]
- [HttpPost]
+ [HttpGet("logout")]
public ActionResult Logout() {
if (isLoggedIn()) {
- BoredCareersJWT.SignOut(Response);
- return Ok();
+ signOut();
+ return Redirect("/");
}
return NotFound();
}
- [Route("sendverifyemail")]
- [HttpPost]
- public async Task> SendVerify([FromForm] string UserName) {
- try {
- string key = "v" + UserName;
- // Stop from sending multiple emails quickly
- if (_emailContext._SentEmails.ContainsKey(key)) {
- DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key);
- if (PreviousSentTime.AddMinutes(5) > DateTime.Now) {
- return NotFound("Cannot sent another verify email until 5 minutes has elapsed");
- }
- else {
- _emailContext._SentEmails.Remove(key);
- }
- }
- Account? test = await _databaseService.GetAccount(UserName.ToLower());
- if (test != null) {
- test.EmailToken = Guid.NewGuid().ToString();
- await _databaseService.SetAccount(test);
-
- string EmailContents = EmailService.VerifyEmailEmail;
- EmailContents = Substitue(EmailContents, "@UserName", UserName);
- EmailContents = Substitue(EmailContents, "@UserName", UserName);
- EmailContents = Substitue(EmailContents, "@VerifyPassword", test.EmailToken);
-
- string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents);
- _emailContext._SentEmails.Add(key, DateTime.Now);
- return Ok(result);
- }
- return NotFound("Account not found");
- } catch (Exception) {
- return NotFound("An internal server error has occured");
- }
- }
-
- [Route("verifyemail")]
- [HttpPost]
- public async Task> VerifyEmail([FromForm] string UserName, [FromForm] string EmailToken) {
- try {
- Account? test = await _databaseService.GetAccount(UserName.ToLower());
- if (test != null) {
- if (!string.IsNullOrEmpty(test.EmailToken) && test.EmailToken == EmailToken) {
- test.EmailToken = "";
- test.EmailVerified = true;
- await _databaseService.SetAccount(test);
- return Ok(true);
- }
- }
- return NotFound("Account not found or token is invalid");;
- } catch {
- return NotFound("An internal server error has occured");
- }
- }
-
- [Route("sendresetpassword")]
- [HttpPost]
- public async Task> ResetPassword([FromForm] string Email) {
- try {
- string key = "p" + Email.ToLower();
- // Stop from sending multiple emails quickly
- if (_emailContext._SentEmails.ContainsKey(key)) {
- DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key);
- if (PreviousSentTime.AddMinutes(5) > DateTime.Now) {
- return NotFound("Cannot sent another reset requests until 5 minutes has elapsed");
- }
- else {
- _emailContext._SentEmails.Remove(key);
- }
- }
- Account? test = await _databaseService.GetAccount(Email.ToLower());
- if (test != null) {
- test.EmailToken = Guid.NewGuid().ToString();
- await _databaseService.SetAccount(test);
-
- string EmailContents = EmailService.ResetPasswordEmail;
- EmailContents = Substitue(EmailContents, "@UserName", test.UserName);
- EmailContents = Substitue(EmailContents, "@UserName", test.UserName);
- EmailContents = Substitue(EmailContents, "@ResetPassWord", test.EmailToken);
-
- string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents);
- _emailContext._SentEmails.Add(key, DateTime.Now);
- return Ok(result);
- }
- return NotFound("Account Not Found");
- } catch (Exception e) {
- Console.WriteLine("EmailService Error: " + e.ToString());
- return NotFound("An internal server error has occured");
- }
-
- }
-
- [Route("resetpassword")]
- [HttpPost]
- public async Task> ResetPwdVerify([FromForm] string UserName, [FromForm] string NewPassword, [FromForm] string ResetToken) {
- try {
- Account? test = await _databaseService.GetAccount(UserName.ToLower());
- if (test != null && !string.IsNullOrEmpty(test.EmailToken)) {
- if (!string.IsNullOrEmpty(test.EmailToken) && test.EmailToken == ResetToken) {
- test.CurrentPasswordAttempts = 0;
- test.EmailToken = "";
- test.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword);
- await _databaseService.SetAccount(test);
- return Ok(true);
- }
- }
- return NotFound("Account not found or reset token is bad");
- } catch {
- return NotFound("An internal server error has occured");
- }
- }
-
- [Route("delete")]
- [HttpPost]
- public async Task delete([FromForm] string Password) {
- try {
- if (isLoggedIn()) {
- Account user = await getLoggedInUser();
- if (BCrypt.Net.BCrypt.Verify(Password, user.PasswordHash)) {
- await _databaseService.DeleteAccount(user.ID);
- return Ok();
- }
- }
- return NotFound("User is not logged in");
- } catch (Exception ex) {
- Console.WriteLine("Delete Error: " + ex.Message);
- return NotFound("An internal server error has occured");
- }
- }
-
}
+
}
diff --git a/src/Server/Controllers/MistoxControllerBase.cs b/src/Server/Controllers/MistoxControllerBase.cs
index eb502ca..9ea558b 100644
--- a/src/Server/Controllers/MistoxControllerBase.cs
+++ b/src/Server/Controllers/MistoxControllerBase.cs
@@ -13,6 +13,19 @@ namespace BoredCareers.Controllers {
_databaseService = databaseService;
}
+ public void signIn(string JWT) {
+ Response.Cookies.Append("mistox_session", JWT, new CookieOptions {
+ Secure = true,
+ HttpOnly = true,
+ SameSite = SameSiteMode.Strict,
+ Expires = DateTime.UtcNow.AddDays(7)
+ });
+ }
+
+ public void signOut() {
+ Response.Cookies.Delete("mistox_session");
+ }
+
public bool isLoggedIn() {
if (User.Identity != null && User.Identity.IsAuthenticated) {
return true;
@@ -24,13 +37,16 @@ namespace BoredCareers.Controllers {
return Convert.ToInt32(User.FindFirstValue(ClaimTypes.NameIdentifier));
}
- public async Task getLoggedInUser() {
+ public Account getLoggedInUser() {
try {
- Account? test = await _databaseService.GetAccount(getLoggedInUserID());
- if (test != null) {
- return test;
- }
- return new Account();
+ Account building = new Account {
+ ID = Convert.ToInt32(User.FindFirstValue(ClaimTypes.NameIdentifier)),
+ UserName = User.FindFirstValue(ClaimTypes.Name)!.ToString(),
+ Email = User.FindFirstValue(ClaimTypes.Email)!.ToString(),
+ Role = User.FindFirstValue(ClaimTypes.Role)!.ToString(),
+ DataServer = User.FindFirstValue(ClaimTypes.UserData)!.ToString()
+ };
+ return building;
} catch {
return new Account();
}
diff --git a/src/Server/Program.cs b/src/Server/Program.cs
index d5beb5c..1ec73c9 100755
--- a/src/Server/Program.cs
+++ b/src/Server/Program.cs
@@ -7,7 +7,7 @@ using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
-using System.Text;
+using System.Security.Cryptography;
var builder = WebApplication.CreateBuilder(args);
@@ -39,15 +39,6 @@ string dbPass = !string.IsNullOrEmpty(_dbpass) ? _dbpass : "oasv34$8gpv023dd";
DatabaseService databaseService = new DatabaseService(connectionString: "server=" + dbserver + ";user=" + dbUser + ";database=" + dbdatabase + ";password=" + dbPass + ";port=3306;");
builder.Services.Add( new ServiceDescriptor( typeof( DatabaseService ), databaseService ) );
-////////////////////////////////
-////////// Auth Service ////////
-////////////////////////////////
-
-// Address
-string? _jwtSecret = Environment.GetEnvironmentVariable("JWTsecret");
-string JWTsecret = !string.IsNullOrEmpty(_jwtSecret) ? _jwtSecret : "v0Ftluhdh7Nht8^2b5eaiC^IS^VS1ku0VBs3j*B2";
-BoredCareersJWT.TokenSecretKey = JWTsecret;
-
////////////////////////////////
///////// Email Service ////////
////////////////////////////////
@@ -92,7 +83,26 @@ if (IPayment._PaymentType == PaymentType.StripeIntent) {
IPayment._EndpointSecret = string.IsNullOrEmpty(StripeEndpointKey) ? "" : StripeEndpointKey;
}
-// Authentication Service
+////////////////////////////////
+/////// Auth Service ////////
+////////////////////////////////
+
+RsaSecurityKey? PublicKey = null;
+using (HttpClient client = new HttpClient()) {
+ HttpResponseMessage PublicKeyResponse = await client.GetAsync("https://auth.mistox.com/api/auth/publickey");
+ if (PublicKeyResponse.IsSuccessStatusCode) {
+ string publicKey = await PublicKeyResponse.Content.ReadAsStringAsync();
+ RSA rsa = RSA.Create();
+ rsa.ImportFromPem(publicKey);
+ PublicKey = new RsaSecurityKey(rsa);
+ }
+}
+
+if (PublicKey == null) {
+ Console.WriteLine("Unable to load RSA PubKey Shutting Down");
+ Environment.Exit(100);
+}
+
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
@@ -102,14 +112,14 @@ builder.Services.AddAuthentication(options => {
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
- ValidIssuer = BoredCareersJWT.TokenIssuer,
- ValidAudience = BoredCareersJWT.TokenAudience,
- IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(BoredCareersJWT.TokenSecretKey)),
+ ValidIssuer = "https://auth.mistox.com",
+ ValidAudience = "mistox-llc-auth-token",
+ IssuerSigningKey = PublicKey,
ClockSkew = TimeSpan.FromMinutes(1)
};
options.Events = new JwtBearerEvents {
OnMessageReceived = context => {
- context.Token = context.Request.Cookies[BoredCareersJWT.TokenName];
+ context.Token = context.Request.Cookies["mistox_session"];
return Task.CompletedTask;
},
OnTokenValidated = context => {
@@ -118,10 +128,7 @@ builder.Services.AddAuthentication(options => {
var exp = jwtToken.ValidTo;
var now = DateTime.UtcNow;
if ((exp - now) < TimeSpan.FromDays(3)) {
- int accountID = Convert.ToInt32(context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value);
- bool isPersistent = bool.Parse(context.Principal?.FindFirst(ClaimTypes.IsPersistent)?.Value);
- var newJWT = BoredCareersJWT.GenereateJWTToken(accountID, isPersistent);
- BoredCareersJWT.SignIn(context.HttpContext.Response, isPersistent, newJWT);
+ // Impliment token refresh
}
}
return Task.CompletedTask;
diff --git a/src/Server/Services/jwt.cs b/src/Server/Services/jwt.cs
deleted file mode 100644
index 21dcf33..0000000
--- a/src/Server/Services/jwt.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System.IdentityModel.Tokens.Jwt;
-using System.Security.Claims;
-using System.Text;
-using Microsoft.IdentityModel.Tokens;
-
-namespace BoredCareers.Services {
- public class BoredCareersJWT {
-
- public static string TokenAudience = "mistox-llc-auth-token";
- public static string TokenIssuer = "https://auth.mistox.com";
- public static string TokenSecretKey = "";
- public static string TokenName = "mistox_session";
-
- public static string GenereateJWTToken(int accountID, bool StayLoggedIn) {
- var tokenHandler = new JwtSecurityTokenHandler();
- var key = Encoding.UTF8.GetBytes(TokenSecretKey);
-
- var tokenDiscriptor = new SecurityTokenDescriptor {
- Subject = new ClaimsIdentity([
- new Claim(ClaimTypes.NameIdentifier, accountID.ToString()),
- new Claim(ClaimTypes.IsPersistent, StayLoggedIn.ToString())
- ]),
- Expires = DateTime.UtcNow.AddDays(7),
- IssuedAt = DateTime.UtcNow,
- SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256),
- Audience = TokenAudience,
- Issuer = TokenIssuer
- };
-
- var token = tokenHandler.CreateToken(tokenDiscriptor);
- return tokenHandler.WriteToken(token);
- }
-
- public static void SignIn(HttpResponse Response, bool StayLoggedIn, string jwt) {
- if (StayLoggedIn) {
- // Stay logged in cookie
- Response.Cookies.Append(TokenName, jwt, new CookieOptions {
- Secure = true,
- HttpOnly = true,
- SameSite = SameSiteMode.Strict,
- Expires = DateTime.UtcNow.AddDays(7)
- });
- } else {
- // Session cookie
- Response.Cookies.Append(TokenName, jwt, new CookieOptions {
- Secure = true,
- HttpOnly = true,
- SameSite = SameSiteMode.Strict,
- });
- }
- }
-
- public static void SignOut(HttpResponse Response) {
- Response.Cookies.Delete(TokenName);
- }
- }
-}
\ No newline at end of file