dhomie-app 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +219 -32
- package/fesm2022/dhomie-app.mjs +11 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,63 +1,250 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dhomie-app
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A self-contained Angular micro-app library that handles OIDC authentication (PKCE) and the DHomie feature set. It works in both a Capacitor native shell (iOS / Android) and a plain web shell.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The library ships with OIDC credentials and API URLs **baked in** at publish time — no runtime configuration is required from the consumer.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
1. [Requirements](#1-requirements)
|
|
12
|
+
2. [Installation](#2-installation)
|
|
13
|
+
3. [Register the micro route in the shell app](#3-register-the-micro-route-in-the-shell-app)
|
|
14
|
+
4. [Add the cold-start deep link handler (Capacitor only)](#4-add-the-cold-start-deep-link-handler-capacitor-only)
|
|
15
|
+
5. [Register the custom URL scheme (Capacitor native only)](#5-register-the-custom-url-scheme-capacitor-native-only)
|
|
16
|
+
6. [Making API requests inside micro components](#6-making-api-requests-inside-micro-components)
|
|
17
|
+
7. [How authentication works](#7-how-authentication-works)
|
|
18
|
+
8. [Baked-in configuration reference](#8-baked-in-configuration-reference)
|
|
19
|
+
9. [Publishing a new version](#9-publishing-a-new-version)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. Requirements
|
|
24
|
+
|
|
25
|
+
| Peer dependency | Version |
|
|
26
|
+
|-------------------------|----------|
|
|
27
|
+
| `@angular/common` | `^20.3.0` |
|
|
28
|
+
| `@angular/core` | `^20.3.0` |
|
|
29
|
+
| `@angular/router` | `^20.3.0` |
|
|
30
|
+
| `@capacitor/app` | `^7.0.0` |
|
|
31
|
+
| `@capacitor/browser` | `^7.0.0` |
|
|
32
|
+
| `@capacitor/core` | `^7.0.0` |
|
|
33
|
+
| `@capacitor/preferences`| `^7.0.0` |
|
|
34
|
+
| `@ionic/angular` | `^8.0.0` |
|
|
35
|
+
| `angular-oauth2-oidc` | `^20.0.0` |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2. Installation
|
|
8
40
|
|
|
9
41
|
```bash
|
|
10
|
-
|
|
42
|
+
npm install dhomie-app
|
|
11
43
|
```
|
|
12
44
|
|
|
13
|
-
|
|
45
|
+
Then install all peer dependencies that your shell app does not already have:
|
|
14
46
|
|
|
15
47
|
```bash
|
|
16
|
-
|
|
48
|
+
npm install @angular/common @angular/core @angular/router \
|
|
49
|
+
@capacitor/app @capacitor/browser @capacitor/core @capacitor/preferences \
|
|
50
|
+
@ionic/angular angular-oauth2-oidc
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 3. Register the micro route in the shell app
|
|
56
|
+
|
|
57
|
+
Add a lazy-loaded `micro` route that delegates to `MICRO_ROUTES`. The library self-registers all of its providers (HttpClient, OAuthService, storage, interceptors) inside a child `EnvironmentInjector` — no call to `provideMicroApp()` is needed in the shell.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// src/app/app.routes.ts
|
|
61
|
+
import { Routes } from '@angular/router';
|
|
62
|
+
import { MICRO_ROUTES } from 'dhomie-app'; // ← named export from the library
|
|
63
|
+
|
|
64
|
+
export const routes: Routes = [
|
|
65
|
+
{
|
|
66
|
+
path: 'home',
|
|
67
|
+
loadComponent: () => import('./home/home.page').then((m) => m.HomePage),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
path: 'micro',
|
|
71
|
+
loadChildren: () => import('dhomie-app').then((m) => m.MICRO_ROUTES),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
path: '',
|
|
75
|
+
redirectTo: 'home',
|
|
76
|
+
pathMatch: 'full',
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
That is all the routing setup you need. The library exposes these internal routes under `/micro`:
|
|
82
|
+
|
|
83
|
+
| URL | Purpose |
|
|
84
|
+
|---------------------|----------------------------------------------|
|
|
85
|
+
| `/micro` | Redirects to `/micro/home` |
|
|
86
|
+
| `/micro/home` | Main micro-app view (guarded — login required) |
|
|
87
|
+
| `/micro/callback` | OAuth PKCE callback (no guard) |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 4. Add the cold-start deep link handler (Capacitor only)
|
|
92
|
+
|
|
93
|
+
When the OS kills the app while the in-app browser is open (cold-start scenario), the guard's `appUrlOpen` listener is gone. The shell's `AppComponent` must register its own listener to catch the returning callback URL and navigate to `/micro/callback`.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// src/app/app.component.ts
|
|
97
|
+
import { Component, inject, OnInit } from '@angular/core';
|
|
98
|
+
import { Router } from '@angular/router';
|
|
99
|
+
import { App } from '@capacitor/app';
|
|
100
|
+
import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone';
|
|
101
|
+
|
|
102
|
+
@Component({
|
|
103
|
+
selector: 'app-root',
|
|
104
|
+
templateUrl: 'app.component.html',
|
|
105
|
+
imports: [IonApp, IonRouterOutlet],
|
|
106
|
+
})
|
|
107
|
+
export class AppComponent implements OnInit {
|
|
108
|
+
private readonly router = inject(Router);
|
|
109
|
+
|
|
110
|
+
ngOnInit(): void {
|
|
111
|
+
App.addListener('appUrlOpen', (event) => {
|
|
112
|
+
// Only handle the DHomie custom scheme.
|
|
113
|
+
if (!event.url.startsWith('thehoodapp://')) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Parse query params from the custom-scheme callback URL.
|
|
118
|
+
// e.g. com.thehood.app://micro/callback?code=abc&state=xyz
|
|
119
|
+
const callbackUrl = new URL(event.url);
|
|
120
|
+
const queryParams: Record<string, string> = {};
|
|
121
|
+
callbackUrl.searchParams.forEach((value, key) => {
|
|
122
|
+
queryParams[key] = value;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
this.router.navigate(['/micro/callback'], { queryParams });
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
17
129
|
```
|
|
18
130
|
|
|
19
|
-
|
|
131
|
+
> **Note on cold-start**: if the OS killed the app during the browser session, the PKCE `code_verifier` stored in RAM is lost. The callback component's error handler catches the failed token exchange and redirects to `/micro/home`, which restarts the login flow from scratch. This is the expected and accepted behavior for cold-start.
|
|
20
132
|
|
|
21
|
-
|
|
133
|
+
---
|
|
22
134
|
|
|
23
|
-
|
|
24
|
-
|
|
135
|
+
## 5. Register the custom URL scheme (Capacitor native only)
|
|
136
|
+
|
|
137
|
+
The mobile OAuth redirect URI is `com.thehood.app://micro/callback`. You must register this scheme in both native projects.
|
|
138
|
+
|
|
139
|
+
### iOS — `ios/App/App/Info.plist`
|
|
140
|
+
|
|
141
|
+
```xml
|
|
142
|
+
<key>CFBundleURLTypes</key>
|
|
143
|
+
<array>
|
|
144
|
+
<dict>
|
|
145
|
+
<key>CFBundleURLSchemes</key>
|
|
146
|
+
<array>
|
|
147
|
+
<string>com.thehood.app</string>
|
|
148
|
+
</array>
|
|
149
|
+
</dict>
|
|
150
|
+
</array>
|
|
25
151
|
```
|
|
26
152
|
|
|
27
|
-
|
|
153
|
+
### Android — `android/app/src/main/AndroidManifest.xml`
|
|
28
154
|
|
|
29
|
-
|
|
155
|
+
Inside the main `<activity>` tag:
|
|
30
156
|
|
|
31
|
-
|
|
157
|
+
```xml
|
|
158
|
+
<intent-filter>
|
|
159
|
+
<action android:name="android.intent.action.VIEW" />
|
|
160
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
161
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
162
|
+
<data android:scheme="com.thehood.app" />
|
|
163
|
+
</intent-filter>
|
|
164
|
+
```
|
|
32
165
|
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
cd dist/micro-app
|
|
36
|
-
```
|
|
166
|
+
---
|
|
37
167
|
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
npm publish
|
|
41
|
-
```
|
|
168
|
+
## 6. Making API requests inside micro components
|
|
42
169
|
|
|
43
|
-
|
|
170
|
+
The library provides its own child-scoped `HttpClient`. Use the `IS_MICRO_API` context token on every request so the micro interceptor can:
|
|
44
171
|
|
|
45
|
-
|
|
172
|
+
- Prepend the API base URL to relative paths.
|
|
173
|
+
- Attach the Bearer token.
|
|
174
|
+
- Handle 401 errors with an automatic token refresh.
|
|
46
175
|
|
|
47
|
-
```
|
|
48
|
-
|
|
176
|
+
```ts
|
|
177
|
+
import { HttpClient, HttpContext } from '@angular/common/http';
|
|
178
|
+
import { inject } from '@angular/core';
|
|
179
|
+
import { IS_MICRO_API } from 'dhomie-app';
|
|
180
|
+
|
|
181
|
+
// Inside a micro component or service:
|
|
182
|
+
const http = inject(HttpClient);
|
|
183
|
+
|
|
184
|
+
http.get('/properties', {
|
|
185
|
+
context: new HttpContext().set(IS_MICRO_API, true),
|
|
186
|
+
});
|
|
187
|
+
// → sends GET https://api.dhomie.com/v1/properties
|
|
188
|
+
// with Authorization: Bearer <current_token>
|
|
49
189
|
```
|
|
50
190
|
|
|
51
|
-
|
|
191
|
+
> Requests made **without** `IS_MICRO_API` (or with it set to `false`) pass through unchanged — parent-app interceptors are not involved in micro-app calls, and vice versa.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 7. How authentication works
|
|
196
|
+
|
|
197
|
+
The guard (`microAuthGuard`) runs before every protected route under `/micro/home`.
|
|
198
|
+
|
|
199
|
+
**Step-by-step sequence:**
|
|
200
|
+
|
|
201
|
+
1. **Hydrate storage** — token data is loaded from `@capacitor/preferences` into RAM. This is memoized and instant on repeat navigations.
|
|
202
|
+
2. **Check token** — if `hasValidAccessToken()` returns `true`, navigation is allowed immediately.
|
|
203
|
+
3. **Load discovery document** — the OIDC `.well-known` endpoint is fetched once and cached.
|
|
204
|
+
4. **Platform split:**
|
|
205
|
+
- **Native (Capacitor)** — generates a PKCE authorization URL, registers an `appUrlOpen` listener, then opens the URL in the system browser via `@capacitor/browser`. Returns `false` (blocks navigation).
|
|
206
|
+
- **Web** — calls `initCodeFlow()` which redirects `window.location` to the authorization endpoint. Returns `false`.
|
|
207
|
+
5. **Callback** — when the auth server redirects back, `MicroCallbackComponent` (at `/micro/callback`) calls `tryLoginCodeFlow()`, exchanges the code for tokens, and stores them via `CapacitorOAuthStorage`.
|
|
208
|
+
6. **Token refresh** — `MicroRefreshSchedulerService` proactively refreshes tokens before expiry. `MicroTokenRefreshService` handles concurrent 401s with a shared refresh lock.
|
|
52
209
|
|
|
53
|
-
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 8. Baked-in configuration reference
|
|
213
|
+
|
|
214
|
+
All OIDC credentials and API URLs are inlined into the published FESM bundle at build time. No runtime configuration is needed. The values correspond to `env.prod.ts`:
|
|
215
|
+
|
|
216
|
+
| Field | Baked-in value |
|
|
217
|
+
|-----------------------------|---------------------------------------------|
|
|
218
|
+
| `oidc.issuer` | `https://auth.dhomie.com` |
|
|
219
|
+
| `oidc.clientId` | `dhomie-micro-client` |
|
|
220
|
+
| `oidc.scope` | `openid profile email offline_access` |
|
|
221
|
+
| `oidc.redirectUri` (web) | `https://app.dhomie.com/micro/callback` |
|
|
222
|
+
| `oidc.mobileRedirectUri` | `com.thehood.app://micro/callback` |
|
|
223
|
+
| `oidc.responseType` | `code` (PKCE) |
|
|
224
|
+
| `microApiBaseUrl` | `https://api.dhomie.com/v1` |
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 9. Publishing a new version
|
|
229
|
+
|
|
230
|
+
Run the publish script from the **project root**:
|
|
54
231
|
|
|
55
232
|
```bash
|
|
56
|
-
|
|
233
|
+
# Without 2FA OTP
|
|
234
|
+
npm run publish:dhomie-app
|
|
235
|
+
|
|
236
|
+
# With 2FA OTP
|
|
237
|
+
npm run publish:dhomie-app -- 123456
|
|
57
238
|
```
|
|
58
239
|
|
|
59
|
-
|
|
240
|
+
The script performs these steps automatically:
|
|
60
241
|
|
|
61
|
-
|
|
242
|
+
| Step | Action |
|
|
243
|
+
|------|--------|
|
|
244
|
+
| 0 | Bumps the **patch** version in `projects/dhomie-app/package.json` |
|
|
245
|
+
| 1 | Builds the library in production mode (`ng build dhomie-app --configuration production`) |
|
|
246
|
+
| 2 | Inlines the `env.prod.ts` values into the FESM bundle (replaces the `dhomie-env` virtual import) |
|
|
247
|
+
| 3 | Removes the stale source map and any `.npmrc` copied into `dist/dhomie-app` |
|
|
248
|
+
| 4 | Publishes `./dist/dhomie-app` to npm with `--access public` |
|
|
62
249
|
|
|
63
|
-
|
|
250
|
+
To bump the **minor** or **major** version instead, edit `projects/dhomie-app/package.json` manually before running the script.
|
package/fesm2022/dhomie-app.mjs
CHANGED
|
@@ -6,18 +6,18 @@ import { Browser } from '@capacitor/browser';
|
|
|
6
6
|
import { Capacitor } from '@capacitor/core';
|
|
7
7
|
import { OAuthService, OAuthStorage, provideOAuthClient } from 'angular-oauth2-oidc';
|
|
8
8
|
const env = {
|
|
9
|
-
production: true,
|
|
10
|
-
oidc: {
|
|
11
|
-
issuer:
|
|
12
|
-
clientId:
|
|
13
|
-
scope:
|
|
14
|
-
redirectUri:
|
|
15
|
-
mobileRedirectUri:
|
|
16
|
-
responseType:
|
|
17
|
-
showDebugInformation:
|
|
18
|
-
useSilentRefresh: false
|
|
9
|
+
"production": true,
|
|
10
|
+
"oidc": {
|
|
11
|
+
"issuer": "https://thehood-dev-uae-api.azurewebsites.net",
|
|
12
|
+
"clientId": "ios",
|
|
13
|
+
"scope": "openid profile offline_access app",
|
|
14
|
+
"redirectUri": "https://thehood-dev-uae-api.azurewebsites.net/login-callback",
|
|
15
|
+
"mobileRedirectUri": "thehoodapp:/callback",
|
|
16
|
+
"responseType": "code",
|
|
17
|
+
"showDebugInformation": true,
|
|
18
|
+
"useSilentRefresh": false
|
|
19
19
|
},
|
|
20
|
-
microApiBaseUrl:
|
|
20
|
+
"microApiBaseUrl": "https://api.dhomie.com/v1"
|
|
21
21
|
};
|
|
22
22
|
import { Preferences } from '@capacitor/preferences';
|
|
23
23
|
import { IonContent, IonSpinner, IonButton, IonButtons, IonHeader, IonItem, IonLabel, IonList, IonTitle, IonToolbar } from '@ionic/angular/standalone';
|