Skip to content

Commit c9058a5

Browse files
committed
feat: angular sample
1 parent c233b06 commit c9058a5

28 files changed

+679
-15
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ node_modules
1212
/packages/*/dist
1313
/packages/*/lib
1414
/packages/*/build
15+
/packages/*/tmp
16+
/packages/*/out-tsc
17+
/packages/*/bazel-out
1518

1619
# logs
1720
logs
@@ -22,13 +25,16 @@ yarn-error.log*
2225
.pnpm-debug.log*
2326

2427
# misc
28+
.angular/cache
29+
.sass-cache/
2530
cache
2631
.*cache
2732
.DS_Store
33+
Thumbs.db
2834
*.env
2935
.idea/
3036
*.pem
3137
.history
3238
.vercel
3339
.next
34-
*.local
40+
*.local

packages/angular-sample/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Logto Angular sample
2+
3+
A sample Angular application that demonstrates how to integrate Logto with `angular-auth-oidc-client`.
4+
5+
**Configuration**: See [app.config.ts](src/app/app.config.ts).
6+
**Usage**: See [app.component.ts](src/app/app.component.ts).
7+
8+
For more information about `angular-auth-oidc-client`, see its [repository](https://github.com/damienbod/angular-auth-oidc-client) and official [documentation](https://angular-auth-oidc-client.com/).
9+
10+
> *[!Note]
11+
> This project is excluded from the workspace. To run the sample, you need to manually install project dependencies.
12+
13+
---
14+
15+
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.2.0.
16+
17+
## Development server
18+
19+
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
20+
21+
## Code scaffolding
22+
23+
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
24+
25+
## Build
26+
27+
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
28+
29+
## Running unit tests
30+
31+
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
32+
33+
## Running end-to-end tests
34+
35+
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.

packages/angular-sample/angular.json

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
{
2+
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3+
"version": 1,
4+
"newProjectRoot": "projects",
5+
"projects": {
6+
"@logto/angular-sample": {
7+
"projectType": "application",
8+
"schematics": {
9+
"@schematics/angular:component": {
10+
"style": "scss"
11+
}
12+
},
13+
"root": "",
14+
"sourceRoot": "src",
15+
"prefix": "app",
16+
"architect": {
17+
"build": {
18+
"builder": "@angular-devkit/build-angular:application",
19+
"options": {
20+
"outputPath": "dist/logto/angular-sample",
21+
"index": "src/index.html",
22+
"browser": "src/main.ts",
23+
"polyfills": [
24+
"zone.js"
25+
],
26+
"tsConfig": "tsconfig.app.json",
27+
"inlineStyleLanguage": "scss",
28+
"assets": [
29+
"src/favicon.ico",
30+
"src/assets"
31+
],
32+
"styles": [
33+
"src/styles.scss"
34+
],
35+
"scripts": [],
36+
"server": "src/main.server.ts",
37+
"prerender": true,
38+
"ssr": {
39+
"entry": "server.ts"
40+
}
41+
},
42+
"configurations": {
43+
"production": {
44+
"budgets": [
45+
{
46+
"type": "initial",
47+
"maximumWarning": "500kb",
48+
"maximumError": "1mb"
49+
},
50+
{
51+
"type": "anyComponentStyle",
52+
"maximumWarning": "2kb",
53+
"maximumError": "4kb"
54+
}
55+
],
56+
"outputHashing": "all"
57+
},
58+
"development": {
59+
"optimization": false,
60+
"extractLicenses": false,
61+
"sourceMap": true
62+
}
63+
},
64+
"defaultConfiguration": "production"
65+
},
66+
"serve": {
67+
"builder": "@angular-devkit/build-angular:dev-server",
68+
"configurations": {
69+
"production": {
70+
"buildTarget": "@logto/angular-sample:build:production"
71+
},
72+
"development": {
73+
"buildTarget": "@logto/angular-sample:build:development"
74+
}
75+
},
76+
"defaultConfiguration": "development"
77+
},
78+
"extract-i18n": {
79+
"builder": "@angular-devkit/build-angular:extract-i18n",
80+
"options": {
81+
"buildTarget": "@logto/angular-sample:build"
82+
}
83+
},
84+
"test": {
85+
"builder": "@angular-devkit/build-angular:karma",
86+
"options": {
87+
"polyfills": [
88+
"zone.js",
89+
"zone.js/testing"
90+
],
91+
"tsConfig": "tsconfig.spec.json",
92+
"inlineStyleLanguage": "scss",
93+
"assets": [
94+
"src/favicon.ico",
95+
"src/assets"
96+
],
97+
"styles": [
98+
"src/styles.scss"
99+
],
100+
"scripts": []
101+
}
102+
}
103+
}
104+
}
105+
}
106+
}

packages/angular-sample/package.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "@logto/angular-sample",
3+
"version": "0.0.0",
4+
"scripts": {
5+
"ng": "ng",
6+
"start": "ng serve",
7+
"build": "ng build",
8+
"watch": "ng build --watch --configuration development",
9+
"test": "ng test",
10+
"serve:ssr:@logto/angular-sample": "node dist/logto/angular-sample/server/server.mjs"
11+
},
12+
"private": true,
13+
"dependencies": {
14+
"@angular/animations": "^17.2.0",
15+
"@angular/common": "^17.2.0",
16+
"@angular/compiler": "^17.2.0",
17+
"@angular/core": "^17.2.0",
18+
"@angular/forms": "^17.2.0",
19+
"@angular/platform-browser": "^17.2.0",
20+
"@angular/platform-browser-dynamic": "^17.2.0",
21+
"@angular/platform-server": "^17.2.0",
22+
"@angular/router": "^17.2.0",
23+
"@angular/ssr": "^17.2.0",
24+
"@logto/js": "workspace:^",
25+
"angular-auth-oidc-client": "^17.0.0",
26+
"express": "^4.18.2",
27+
"rxjs": "~7.8.0",
28+
"tslib": "^2.3.0",
29+
"zone.js": "~0.14.3"
30+
},
31+
"devDependencies": {
32+
"@angular-devkit/build-angular": "^17.2.0",
33+
"@angular/cli": "^17.2.0",
34+
"@angular/compiler-cli": "^17.2.0",
35+
"@types/express": "^4.17.17",
36+
"@types/jasmine": "~5.1.0",
37+
"@types/node": "^18.18.0",
38+
"jasmine-core": "~5.1.0",
39+
"karma": "~6.4.0",
40+
"karma-chrome-launcher": "~3.2.0",
41+
"karma-coverage": "~2.2.0",
42+
"karma-jasmine": "~5.1.0",
43+
"karma-jasmine-html-reporter": "~2.1.0",
44+
"typescript": "~5.3.2"
45+
}
46+
}

packages/angular-sample/server.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { APP_BASE_HREF } from '@angular/common';
2+
import { CommonEngine } from '@angular/ssr';
3+
import express from 'express';
4+
import { fileURLToPath } from 'node:url';
5+
import { dirname, join, resolve } from 'node:path';
6+
import bootstrap from './src/main.server';
7+
8+
// The Express app is exported so that it can be used by serverless Functions.
9+
export function app(): express.Express {
10+
const server = express();
11+
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
12+
const browserDistFolder = resolve(serverDistFolder, '../browser');
13+
const indexHtml = join(serverDistFolder, 'index.server.html');
14+
15+
const commonEngine = new CommonEngine();
16+
17+
server.set('view engine', 'html');
18+
server.set('views', browserDistFolder);
19+
20+
// Example Express Rest API endpoints
21+
// server.get('/api/**', (req, res) => { });
22+
// Serve static files from /browser
23+
server.get('*.*', express.static(browserDistFolder, {
24+
maxAge: '1y'
25+
}));
26+
27+
// All regular routes use the Angular engine
28+
server.get('*', (req, res, next) => {
29+
const { protocol, originalUrl, baseUrl, headers } = req;
30+
31+
commonEngine
32+
.render({
33+
bootstrap,
34+
documentFilePath: indexHtml,
35+
url: `${protocol}://${headers.host}${originalUrl}`,
36+
publicPath: browserDistFolder,
37+
providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
38+
})
39+
.then((html) => res.send(html))
40+
.catch((err) => next(err));
41+
});
42+
43+
return server;
44+
}
45+
46+
function run(): void {
47+
const port = process.env['PORT'] || 4000;
48+
49+
// Start up the Node server
50+
const server = app();
51+
server.listen(port, () => {
52+
console.log(`Node Express server listening on http://localhost:${port}`);
53+
});
54+
}
55+
56+
run();
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<h1>Hello, {{ title }}</h1>
2+
<p>Congratulations! Your app is running. 🎉</p>
3+
<button *ngIf="!isAuthenticated" (click)="signIn()">Sign in</button>
4+
<ng-container *ngIf="isAuthenticated">
5+
<pre>{{ userData | json }}</pre>
6+
<p>Access token: {{ accessToken }}</p>
7+
<button (click)="signOut()">Sign out</button>
8+
</ng-container>
9+
<router-outlet />

packages/angular-sample/src/app/app.component.scss

Whitespace-only changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { TestBed } from '@angular/core/testing';
2+
import { AppComponent } from './app.component';
3+
4+
describe('AppComponent', () => {
5+
beforeEach(async () => {
6+
await TestBed.configureTestingModule({
7+
imports: [AppComponent],
8+
}).compileComponents();
9+
});
10+
11+
it('should create the app', () => {
12+
const fixture = TestBed.createComponent(AppComponent);
13+
const app = fixture.componentInstance;
14+
expect(app).toBeTruthy();
15+
});
16+
17+
it(`should have the '@logto/angular-sample' title`, () => {
18+
const fixture = TestBed.createComponent(AppComponent);
19+
const app = fixture.componentInstance;
20+
expect(app.title).toEqual('@logto/angular-sample');
21+
});
22+
23+
it('should render title', () => {
24+
const fixture = TestBed.createComponent(AppComponent);
25+
fixture.detectChanges();
26+
const compiled = fixture.nativeElement as HTMLElement;
27+
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, @logto/angular-sample');
28+
});
29+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { RouterOutlet } from '@angular/router';
3+
import { CommonModule } from '@angular/common';
4+
import { OidcSecurityService } from 'angular-auth-oidc-client';
5+
import type { UserInfoResponse } from '@logto/js';
6+
7+
@Component({
8+
selector: 'app-root',
9+
standalone: true,
10+
imports: [RouterOutlet, CommonModule],
11+
templateUrl: './app.component.html',
12+
styleUrl: './app.component.scss'
13+
})
14+
export class AppComponent implements OnInit {
15+
title = '@logto/angular-sample';
16+
isAuthenticated = false;
17+
userData?: UserInfoResponse;
18+
idToken?: string;
19+
accessToken?: string;
20+
21+
constructor(public oidcSecurityService: OidcSecurityService) { }
22+
23+
ngOnInit() {
24+
this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, userData, idToken, accessToken }) => {
25+
console.log('app authenticated', isAuthenticated, userData);
26+
this.isAuthenticated = isAuthenticated;
27+
this.userData = userData;
28+
this.idToken = idToken;
29+
this.accessToken = accessToken;
30+
});
31+
}
32+
33+
signIn() {
34+
this.oidcSecurityService.authorize();
35+
}
36+
37+
signOut() {
38+
this.oidcSecurityService.logoff().subscribe((result) => {
39+
console.log('app sign-out', result);
40+
this.isAuthenticated = false;
41+
});
42+
}
43+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
2+
import { provideServerRendering } from '@angular/platform-server';
3+
import { appConfig } from './app.config';
4+
5+
const serverConfig: ApplicationConfig = {
6+
providers: [
7+
provideServerRendering()
8+
]
9+
};
10+
11+
export const config = mergeApplicationConfig(appConfig, serverConfig);

0 commit comments

Comments
 (0)