diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index e529d68ac..9cf5f4200 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,10 +1,10 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
-import { SidenavComponent } from '@osf/sidenav/sidenav.component';
+import { SidenavComponent } from '@core/components/sidenav/sidenav.component';
import { RouterOutlet } from '@angular/router';
-import { HeaderComponent } from '@osf/header/header.component';
-import { MainContentComponent } from '@osf/main-content/main-content.component';
-import { FooterComponent } from '@osf/footer/footer.component';
-import { TopnavComponent } from '@osf/topnav/topnav.component';
+import { HeaderComponent } from '@core/components/header/header.component';
+import { MainContentComponent } from '@core/components/main-content/main-content.component';
+import { FooterComponent } from '@core/components/footer/footer.component';
+import { TopnavComponent } from '@core/components/topnav/topnav.component';
import { IS_PORTRAIT } from '@shared/utils/breakpoints.tokens';
import { toSignal } from '@angular/core/rxjs-interop';
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
new file mode 100644
index 000000000..87ec29d5d
--- /dev/null
+++ b/src/app/app.module.ts
@@ -0,0 +1,8 @@
+import { NgModule } from '@angular/core';
+import { NgxsModule } from '@ngxs/store';
+import { AuthState } from '@core/store/auth';
+
+@NgModule({
+ imports: [NgxsModule.forRoot([AuthState])],
+})
+export class AppModule {}
diff --git a/src/app/footer/footer.component.html b/src/app/core/components/footer/footer.component.html
similarity index 100%
rename from src/app/footer/footer.component.html
rename to src/app/core/components/footer/footer.component.html
diff --git a/src/app/footer/footer.component.scss b/src/app/core/components/footer/footer.component.scss
similarity index 100%
rename from src/app/footer/footer.component.scss
rename to src/app/core/components/footer/footer.component.scss
diff --git a/src/app/footer/footer.component.spec.ts b/src/app/core/components/footer/footer.component.spec.ts
similarity index 100%
rename from src/app/footer/footer.component.spec.ts
rename to src/app/core/components/footer/footer.component.spec.ts
diff --git a/src/app/footer/footer.component.ts b/src/app/core/components/footer/footer.component.ts
similarity index 100%
rename from src/app/footer/footer.component.ts
rename to src/app/core/components/footer/footer.component.ts
diff --git a/src/app/header/header.component.html b/src/app/core/components/header/header.component.html
similarity index 100%
rename from src/app/header/header.component.html
rename to src/app/core/components/header/header.component.html
diff --git a/src/app/header/header.component.scss b/src/app/core/components/header/header.component.scss
similarity index 100%
rename from src/app/header/header.component.scss
rename to src/app/core/components/header/header.component.scss
diff --git a/src/app/header/header.component.spec.ts b/src/app/core/components/header/header.component.spec.ts
similarity index 100%
rename from src/app/header/header.component.spec.ts
rename to src/app/core/components/header/header.component.spec.ts
diff --git a/src/app/header/header.component.ts b/src/app/core/components/header/header.component.ts
similarity index 100%
rename from src/app/header/header.component.ts
rename to src/app/core/components/header/header.component.ts
diff --git a/src/app/main-content/main-content.component.html b/src/app/core/components/main-content/main-content.component.html
similarity index 50%
rename from src/app/main-content/main-content.component.html
rename to src/app/core/components/main-content/main-content.component.html
index aec502cfa..924dcc79b 100644
--- a/src/app/main-content/main-content.component.html
+++ b/src/app/core/components/main-content/main-content.component.html
@@ -1,3 +1,3 @@
- main-content works!
+ main-content works!
diff --git a/src/app/main-content/main-content.component.scss b/src/app/core/components/main-content/main-content.component.scss
similarity index 100%
rename from src/app/main-content/main-content.component.scss
rename to src/app/core/components/main-content/main-content.component.scss
diff --git a/src/app/main-content/main-content.component.spec.ts b/src/app/core/components/main-content/main-content.component.spec.ts
similarity index 100%
rename from src/app/main-content/main-content.component.spec.ts
rename to src/app/core/components/main-content/main-content.component.spec.ts
diff --git a/src/app/main-content/main-content.component.ts b/src/app/core/components/main-content/main-content.component.ts
similarity index 100%
rename from src/app/main-content/main-content.component.ts
rename to src/app/core/components/main-content/main-content.component.ts
diff --git a/src/app/sidenav/sidenav.component.html b/src/app/core/components/sidenav/sidenav.component.html
similarity index 100%
rename from src/app/sidenav/sidenav.component.html
rename to src/app/core/components/sidenav/sidenav.component.html
diff --git a/src/app/sidenav/sidenav.component.scss b/src/app/core/components/sidenav/sidenav.component.scss
similarity index 100%
rename from src/app/sidenav/sidenav.component.scss
rename to src/app/core/components/sidenav/sidenav.component.scss
diff --git a/src/app/sidenav/sidenav.component.spec.ts b/src/app/core/components/sidenav/sidenav.component.spec.ts
similarity index 100%
rename from src/app/sidenav/sidenav.component.spec.ts
rename to src/app/core/components/sidenav/sidenav.component.spec.ts
diff --git a/src/app/sidenav/sidenav.component.ts b/src/app/core/components/sidenav/sidenav.component.ts
similarity index 100%
rename from src/app/sidenav/sidenav.component.ts
rename to src/app/core/components/sidenav/sidenav.component.ts
diff --git a/src/app/topnav/topnav.component.html b/src/app/core/components/topnav/topnav.component.html
similarity index 100%
rename from src/app/topnav/topnav.component.html
rename to src/app/core/components/topnav/topnav.component.html
diff --git a/src/app/topnav/topnav.component.scss b/src/app/core/components/topnav/topnav.component.scss
similarity index 100%
rename from src/app/topnav/topnav.component.scss
rename to src/app/core/components/topnav/topnav.component.scss
diff --git a/src/app/topnav/topnav.component.spec.ts b/src/app/core/components/topnav/topnav.component.spec.ts
similarity index 100%
rename from src/app/topnav/topnav.component.spec.ts
rename to src/app/core/components/topnav/topnav.component.spec.ts
diff --git a/src/app/topnav/topnav.component.ts b/src/app/core/components/topnav/topnav.component.ts
similarity index 100%
rename from src/app/topnav/topnav.component.ts
rename to src/app/core/components/topnav/topnav.component.ts
diff --git a/src/app/core/store/auth/auth.actions.ts b/src/app/core/store/auth/auth.actions.ts
new file mode 100644
index 000000000..112d4d3c5
--- /dev/null
+++ b/src/app/core/store/auth/auth.actions.ts
@@ -0,0 +1,9 @@
+export class SetAuthToken {
+ static readonly type = '[Auth] Set Auth Token';
+
+ constructor(public accessToken: string) {}
+}
+
+export class ClearAuth {
+ static readonly type = '[Auth] Clear Auth';
+}
diff --git a/src/app/core/store/auth/auth.model.ts b/src/app/core/store/auth/auth.model.ts
new file mode 100644
index 000000000..17abed7c1
--- /dev/null
+++ b/src/app/core/store/auth/auth.model.ts
@@ -0,0 +1,4 @@
+export interface AuthStateModel {
+ accessToken: string | null;
+ isAuthenticated: boolean;
+}
diff --git a/src/app/core/store/auth/auth.selectors.ts b/src/app/core/store/auth/auth.selectors.ts
new file mode 100644
index 000000000..f50c3b525
--- /dev/null
+++ b/src/app/core/store/auth/auth.selectors.ts
@@ -0,0 +1,15 @@
+import { Selector } from '@ngxs/store';
+import { AuthState } from './auth.state';
+import { AuthStateModel } from './auth.model';
+
+export class AuthSelectors {
+ @Selector([AuthState])
+ static isAuthenticated(state: AuthStateModel): boolean {
+ return state.isAuthenticated;
+ }
+
+ @Selector([AuthState])
+ static getAuthToken(state: AuthStateModel): string | null {
+ return state.accessToken;
+ }
+}
diff --git a/src/app/core/store/auth/auth.state.ts b/src/app/core/store/auth/auth.state.ts
new file mode 100644
index 000000000..9d14b0b4b
--- /dev/null
+++ b/src/app/core/store/auth/auth.state.ts
@@ -0,0 +1,30 @@
+import { Injectable } from '@angular/core';
+import { State, Action, StateContext } from '@ngxs/store';
+import { AuthStateModel } from './auth.model';
+import { SetAuthToken, ClearAuth } from './auth.actions';
+
+@State({
+ name: 'auth',
+ defaults: {
+ accessToken: null,
+ isAuthenticated: false,
+ },
+})
+@Injectable()
+export class AuthState {
+ @Action(SetAuthToken)
+ setAuthToken(ctx: StateContext, action: SetAuthToken) {
+ ctx.patchState({
+ accessToken: action.accessToken,
+ isAuthenticated: true,
+ });
+ }
+
+ @Action(ClearAuth)
+ clearAuth(ctx: StateContext) {
+ ctx.patchState({
+ accessToken: null,
+ isAuthenticated: false,
+ });
+ }
+}
diff --git a/src/app/core/store/auth/index.ts b/src/app/core/store/auth/index.ts
new file mode 100644
index 000000000..bccd7d73b
--- /dev/null
+++ b/src/app/core/store/auth/index.ts
@@ -0,0 +1,4 @@
+export * from './auth.actions';
+export * from './auth.model';
+export * from './auth.selectors';
+export * from './auth.state';
diff --git a/src/app/features/auth/auth.entity.ts b/src/app/features/auth/auth.entity.ts
new file mode 100644
index 000000000..48cafe8b4
--- /dev/null
+++ b/src/app/features/auth/auth.entity.ts
@@ -0,0 +1,8 @@
+export interface LoginCredentials {
+ email: string;
+ password: string;
+}
+
+export interface AuthResponse {
+ accessToken: string;
+}
diff --git a/src/app/features/auth/auth.service.ts b/src/app/features/auth/auth.service.ts
new file mode 100644
index 000000000..7bacfdd15
--- /dev/null
+++ b/src/app/features/auth/auth.service.ts
@@ -0,0 +1,54 @@
+import { Injectable, inject } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Store } from '@ngxs/store';
+import { firstValueFrom } from 'rxjs';
+import { LoginCredentials, AuthResponse } from './auth.entity';
+import { SetAuthToken, ClearAuth } from '@core/store/auth';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class AuthService {
+ private readonly API_URL = 'VALID_API_URL';
+ private readonly AUTH_TOKEN_KEY = '';
+
+ private readonly http: HttpClient = inject(HttpClient);
+ private readonly store: Store = inject(Store);
+
+ //TODO: rewrite/refactor methods according to the API
+ async login(credentials: LoginCredentials): Promise {
+ try {
+ const response = await firstValueFrom(
+ this.http.post(`${this.API_URL}/auth/login`, credentials),
+ );
+
+ if (response.accessToken) {
+ this.setAuthToken(response.accessToken);
+ this.store.dispatch(new SetAuthToken(response.accessToken));
+ }
+ } catch (error) {
+ console.error('Login failed:', error);
+ throw error;
+ }
+ }
+
+ logout(): void {
+ localStorage.removeItem(this.AUTH_TOKEN_KEY);
+ this.store.dispatch(new ClearAuth());
+ }
+
+ getAuthToken(): string | null {
+ return localStorage.getItem(this.AUTH_TOKEN_KEY);
+ }
+
+ private setAuthToken(token: string): void {
+ localStorage.setItem(this.AUTH_TOKEN_KEY, token);
+ }
+
+ private checkInitialAuthState(): void {
+ const token: string | null = this.getAuthToken();
+ if (token) {
+ this.store.dispatch(new SetAuthToken(token));
+ }
+ }
+}
diff --git a/src/app/features/auth/sign-in/sign-in.component.html b/src/app/features/auth/sign-in/sign-in.component.html
new file mode 100644
index 000000000..371a36b70
--- /dev/null
+++ b/src/app/features/auth/sign-in/sign-in.component.html
@@ -0,0 +1 @@
+sign-in works!
diff --git a/src/app/features/auth/sign-in/sign-in.component.scss b/src/app/features/auth/sign-in/sign-in.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/features/auth/sign-in/sign-in.component.spec.ts b/src/app/features/auth/sign-in/sign-in.component.spec.ts
new file mode 100644
index 000000000..d9432eda1
--- /dev/null
+++ b/src/app/features/auth/sign-in/sign-in.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SignInComponent } from './sign-in.component';
+
+describe('SignInComponent', () => {
+ let component: SignInComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [SignInComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(SignInComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/features/auth/sign-in/sign-in.component.ts b/src/app/features/auth/sign-in/sign-in.component.ts
new file mode 100644
index 000000000..9190b9eb0
--- /dev/null
+++ b/src/app/features/auth/sign-in/sign-in.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'osf-sign-in',
+ imports: [],
+ templateUrl: './sign-in.component.html',
+ styleUrl: './sign-in.component.scss',
+})
+export class SignInComponent {}
diff --git a/src/app/features/auth/sign-up/sign-up.component.html b/src/app/features/auth/sign-up/sign-up.component.html
new file mode 100644
index 000000000..7e8b79f67
--- /dev/null
+++ b/src/app/features/auth/sign-up/sign-up.component.html
@@ -0,0 +1 @@
+sign-up works!
diff --git a/src/app/features/auth/sign-up/sign-up.component.scss b/src/app/features/auth/sign-up/sign-up.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/features/auth/sign-up/sign-up.component.spec.ts b/src/app/features/auth/sign-up/sign-up.component.spec.ts
new file mode 100644
index 000000000..6402b2691
--- /dev/null
+++ b/src/app/features/auth/sign-up/sign-up.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SignUpComponent } from './sign-up.component';
+
+describe('SignUpComponent', () => {
+ let component: SignUpComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [SignUpComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(SignUpComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/features/auth/sign-up/sign-up.component.ts b/src/app/features/auth/sign-up/sign-up.component.ts
new file mode 100644
index 000000000..98abe8cbe
--- /dev/null
+++ b/src/app/features/auth/sign-up/sign-up.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'osf-sign-up',
+ imports: [],
+ templateUrl: './sign-up.component.html',
+ styleUrl: './sign-up.component.scss',
+})
+export class SignUpComponent {}