Skip to content

Commit e81a6a5

Browse files
committed
Management-docs
1 parent 5d8e40a commit e81a6a5

5 files changed

Lines changed: 881 additions & 3 deletions

File tree

src/lib/config/navigation.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import {
3737
FileSignature,
3838
Search,
3939
ToggleLeft,
40+
BookOpen,
41+
AppWindow,
4042
} from "@lucide/svelte";
4143
import { env } from "$env/dynamic/public";
4244

@@ -549,6 +551,42 @@ export function getActiveUsersMenuItem(pathname: string) {
549551
return found || usersItems[0];
550552
}
551553

554+
// Management Docs navigation items
555+
function buildManagementDocsItems(): NavigationItem[] {
556+
const items: NavigationItem[] = [
557+
{
558+
href: "/management-docs/consumers",
559+
label: "Consumers",
560+
iconComponent: AppWindow,
561+
},
562+
{
563+
href: "/management-docs/users",
564+
label: "Users",
565+
iconComponent: Users,
566+
},
567+
{
568+
href: "/management-docs/entitlements",
569+
label: "Entitlements",
570+
iconComponent: KeyRound,
571+
},
572+
];
573+
574+
return items;
575+
}
576+
577+
export const managementDocsItems = buildManagementDocsItems();
578+
579+
export function getActiveManagementDocsMenuItem(pathname: string) {
580+
const found = managementDocsItems.find((item) => {
581+
if (item.external) {
582+
return false;
583+
}
584+
return pathname.startsWith(item.href);
585+
});
586+
587+
return found || managementDocsItems[0];
588+
}
589+
552590
// ABAC navigation items
553591
function buildAbacItems(): NavigationItem[] {
554592
const items: NavigationItem[] = [
@@ -628,4 +666,5 @@ export const navSections: NavigationSection[] = [
628666
{ id: "account-access", label: "Account Access", iconComponent: Landmark, items: accountAccessItems, basePaths: ["/account-access", "/mandates"] },
629667
{ id: "dynamic-entities", label: "Dynamic Entities", iconComponent: Box, items: dynamicEntitiesItems, basePaths: ["/dynamic-entities"] },
630668
{ id: "dynamic-endpoints", label: "Dynamic Endpoints", iconComponent: Plug, items: dynamicEndpointsItems, basePaths: ["/dynamic-endpoints"] },
669+
{ id: "management-docs", label: "Management Docs", iconComponent: BookOpen, items: managementDocsItems, basePaths: ["/management-docs"] },
631670
];
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
<script lang="ts">
2+
import {
3+
KeyRound,
4+
Shield,
5+
Lock,
6+
AppWindow,
7+
UserCheck,
8+
Search,
9+
} from "@lucide/svelte";
10+
</script>
11+
12+
<svelte:head>
13+
<title>Consumers Guide - API Manager</title>
14+
</svelte:head>
15+
16+
<div class="container mx-auto px-4 py-8">
17+
<nav class="breadcrumb mb-6">
18+
<a href="/management-docs/consumers" class="breadcrumb-link">Management Docs</a>
19+
<span class="breadcrumb-separator">&gt;</span>
20+
<span class="breadcrumb-current">Consumers</span>
21+
</nav>
22+
23+
<div class="panel">
24+
<div class="panel-header">
25+
<div class="header-content">
26+
<div class="header-icon">
27+
<AppWindow size={32} />
28+
</div>
29+
<div>
30+
<h1 class="panel-title">Consumers</h1>
31+
<div class="panel-subtitle">
32+
Understanding and managing API Consumers (Apps)
33+
</div>
34+
</div>
35+
</div>
36+
</div>
37+
38+
<div class="panel-content">
39+
<div class="intro-grid">
40+
<section class="section">
41+
<h2 class="section-title">What is a Consumer?</h2>
42+
<p class="section-text">
43+
A Consumer is the "App" that calls the OBP API on behalf of an end user or system.
44+
It can be a web application, mobile app, or server-side service. Each Consumer has a
45+
<strong>consumer key</strong> and <strong>secret</strong> which allows it to authenticate
46+
securely with the API server.
47+
</p>
48+
<p class="section-text" style="margin-top: 0.75rem;">
49+
Every Consumer is assigned a <strong>Consumer ID</strong> (a UUID) which appears in
50+
API metrics, logs, and messages to the backend. This makes it possible to track which
51+
application is making which API calls.
52+
</p>
53+
</section>
54+
55+
<section class="section">
56+
<h2 class="section-title">Key Concepts</h2>
57+
<div class="concepts-list">
58+
<div class="concept-item">
59+
<h3 class="concept-title">Consumer Key &amp; Secret</h3>
60+
<p class="concept-text">
61+
Credentials used by the App to authenticate with OBP via OAuth or Direct Login.
62+
The secret must be kept confidential.
63+
</p>
64+
</div>
65+
<div class="concept-item">
66+
<h3 class="concept-title">Consumer ID</h3>
67+
<p class="concept-text">
68+
A UUID that uniquely identifies the Consumer. Appears in metrics and can be used
69+
to filter API call logs.
70+
</p>
71+
</div>
72+
<div class="concept-item">
73+
<h3 class="concept-title">mTLS Certificate Pinning</h3>
74+
<p class="concept-text">
75+
A Consumer can be pinned to an mTLS certificate. After pinning, the App must
76+
present the certificate in all communication with the server.
77+
</p>
78+
</div>
79+
<div class="concept-item">
80+
<h3 class="concept-title">Scopes &amp; Rate Limits</h3>
81+
<p class="concept-text">
82+
Consumers can have scopes (controlling which endpoints they can access) and
83+
rate limits (controlling how many calls they can make).
84+
</p>
85+
</div>
86+
</div>
87+
</section>
88+
</div>
89+
90+
<section class="section workflow-section">
91+
<h2 class="section-title">Common Tasks</h2>
92+
93+
<div class="steps-grid">
94+
<div class="step-card">
95+
<div class="step-header">
96+
<div class="step-number">1</div>
97+
<div class="step-icon">
98+
<Search size={20} />
99+
</div>
100+
</div>
101+
<div class="step-body">
102+
<h3 class="step-title">Find a Consumer</h3>
103+
<p class="step-text">
104+
Use the API Metrics page to identify a Consumer ID from API call logs.
105+
Filter by <code>consumer_id</code> to see all calls from a specific App.
106+
</p>
107+
<a href="/metrics" class="step-link">
108+
Go to Metrics &rarr;
109+
</a>
110+
</div>
111+
</div>
112+
113+
<div class="step-card">
114+
<div class="step-header">
115+
<div class="step-number">2</div>
116+
<div class="step-icon">
117+
<UserCheck size={20} />
118+
</div>
119+
</div>
120+
<div class="step-body">
121+
<h3 class="step-title">Review Consumer Details</h3>
122+
<p class="step-text">
123+
Check who registered the Consumer (developer email), what App name was given,
124+
and whether it has been enabled or is still pending approval.
125+
</p>
126+
</div>
127+
</div>
128+
129+
<div class="step-card">
130+
<div class="step-header">
131+
<div class="step-number">3</div>
132+
<div class="step-icon">
133+
<Shield size={20} />
134+
</div>
135+
</div>
136+
<div class="step-body">
137+
<h3 class="step-title">Manage Scopes</h3>
138+
<p class="step-text">
139+
Control which API endpoints a Consumer can access by managing its scopes.
140+
This is separate from the user's Entitlements (Roles).
141+
</p>
142+
</div>
143+
</div>
144+
145+
<div class="step-card">
146+
<div class="step-header">
147+
<div class="step-number">4</div>
148+
<div class="step-icon">
149+
<Lock size={20} />
150+
</div>
151+
</div>
152+
<div class="step-body">
153+
<h3 class="step-title">Certificate Pinning</h3>
154+
<p class="step-text">
155+
For PSD2 or high-security scenarios, pin the Consumer to an mTLS certificate.
156+
The certificate has a one-to-one relationship with the Consumer.
157+
</p>
158+
</div>
159+
</div>
160+
161+
<div class="step-card">
162+
<div class="step-header">
163+
<div class="step-number">5</div>
164+
<div class="step-icon">
165+
<KeyRound size={20} />
166+
</div>
167+
</div>
168+
<div class="step-body">
169+
<h3 class="step-title">Certificate Renewal</h3>
170+
<p class="step-text">
171+
When a certificate expires, the TPP must register a new Consumer with the new certificate.
172+
Copy rate limits and scopes from the old Consumer to the new one.
173+
</p>
174+
</div>
175+
</div>
176+
</div>
177+
</section>
178+
179+
<section class="section info-section">
180+
<h2 class="section-title">Consumer vs User vs Entitlement</h2>
181+
<p class="section-text">
182+
A <strong>Consumer</strong> represents the App. A <strong>User</strong> is the person
183+
using the App. An <strong>Entitlement</strong> is a Role granted to a User (optionally
184+
scoped to a specific bank). When an API call is made, OBP checks both the Consumer's
185+
scopes and the User's Entitlements to determine access.
186+
</p>
187+
</section>
188+
</div>
189+
</div>
190+
</div>
191+
192+
<style>
193+
.container { max-width: 1200px; }
194+
.breadcrumb { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; }
195+
.breadcrumb-link { color: #3b82f6; text-decoration: none; }
196+
.breadcrumb-link:hover { text-decoration: underline; }
197+
:global([data-mode="dark"]) .breadcrumb-link { color: rgb(var(--color-primary-400)); }
198+
.breadcrumb-separator { color: #9ca3af; }
199+
:global([data-mode="dark"]) .breadcrumb-separator { color: var(--color-surface-500); }
200+
.breadcrumb-current { color: #6b7280; }
201+
:global([data-mode="dark"]) .breadcrumb-current { color: var(--color-surface-400); }
202+
203+
.panel { background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); overflow: hidden; }
204+
:global([data-mode="dark"]) .panel { background: rgb(var(--color-surface-800)); }
205+
.panel-header { padding: 2rem; border-bottom: 1px solid #e5e7eb; }
206+
:global([data-mode="dark"]) .panel-header { border-bottom-color: rgb(var(--color-surface-700)); }
207+
.header-content { display: flex; align-items: center; gap: 1rem; }
208+
.header-icon { display: flex; align-items: center; justify-content: center; width: 64px; height: 64px; background: #eff6ff; color: #3b82f6; border-radius: 12px; flex-shrink: 0; }
209+
:global([data-mode="dark"]) .header-icon { background: rgba(59,130,246,0.2); color: rgb(var(--color-primary-400)); }
210+
.panel-title { font-size: 1.5rem; font-weight: 700; color: #111827; margin: 0; }
211+
:global([data-mode="dark"]) .panel-title { color: var(--color-surface-100); }
212+
.panel-subtitle { font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem; }
213+
:global([data-mode="dark"]) .panel-subtitle { color: var(--color-surface-400); }
214+
.panel-content { padding: 2rem; }
215+
216+
.intro-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin-bottom: 2rem; }
217+
.section { margin-bottom: 0; }
218+
.workflow-section, .info-section { padding-top: 1.5rem; border-top: 1px solid #e5e7eb; }
219+
:global([data-mode="dark"]) .workflow-section, :global([data-mode="dark"]) .info-section { border-top-color: rgb(var(--color-surface-700)); }
220+
.info-section { margin-top: 1.5rem; }
221+
.section-title { font-size: 1.125rem; font-weight: 700; color: #111827; margin: 0 0 0.75rem 0; }
222+
:global([data-mode="dark"]) .section-title { color: var(--color-surface-100); }
223+
.section-text { font-size: 0.875rem; color: #4b5563; line-height: 1.6; margin: 0; }
224+
:global([data-mode="dark"]) .section-text { color: var(--color-surface-300); }
225+
.section-text code { background: #f3f4f6; padding: 0.125rem 0.375rem; border-radius: 4px; font-size: 0.8125rem; }
226+
:global([data-mode="dark"]) .section-text code { background: rgb(var(--color-surface-700)); color: var(--color-surface-200); }
227+
228+
.concepts-list { display: flex; flex-direction: column; gap: 0.75rem; }
229+
.concept-item { padding: 0.75rem; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 6px; }
230+
:global([data-mode="dark"]) .concept-item { background: rgb(var(--color-surface-700)); border-color: rgb(var(--color-surface-600)); }
231+
.concept-title { font-size: 0.8125rem; font-weight: 600; color: #111827; margin: 0 0 0.25rem 0; }
232+
:global([data-mode="dark"]) .concept-title { color: var(--color-surface-100); }
233+
.concept-text { font-size: 0.75rem; color: #4b5563; line-height: 1.4; margin: 0; }
234+
:global([data-mode="dark"]) .concept-text { color: var(--color-surface-300); }
235+
236+
.steps-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 1rem; }
237+
.step-card { display: flex; flex-direction: column; padding: 1rem; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; }
238+
:global([data-mode="dark"]) .step-card { background: rgb(var(--color-surface-700)); border-color: rgb(var(--color-surface-600)); }
239+
.step-header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; }
240+
.step-number { display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; background: #3b82f6; color: white; border-radius: 50%; font-size: 0.75rem; font-weight: 700; flex-shrink: 0; }
241+
.step-icon { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; background: #eff6ff; color: #3b82f6; border-radius: 6px; flex-shrink: 0; }
242+
:global([data-mode="dark"]) .step-icon { background: rgba(59,130,246,0.2); color: rgb(var(--color-primary-400)); }
243+
.step-body { flex: 1; display: flex; flex-direction: column; }
244+
.step-title { font-size: 0.875rem; font-weight: 600; color: #111827; margin: 0 0 0.375rem 0; }
245+
:global([data-mode="dark"]) .step-title { color: var(--color-surface-100); }
246+
.step-text { font-size: 0.75rem; color: #4b5563; line-height: 1.5; margin: 0; flex: 1; }
247+
:global([data-mode="dark"]) .step-text { color: var(--color-surface-300); }
248+
.step-text code { background: #e5e7eb; padding: 0.0625rem 0.25rem; border-radius: 3px; font-size: 0.6875rem; }
249+
:global([data-mode="dark"]) .step-text code { background: rgb(var(--color-surface-600)); color: var(--color-surface-200); }
250+
.step-link { display: inline-block; margin-top: 0.5rem; font-size: 0.75rem; font-weight: 600; color: #3b82f6; text-decoration: none; }
251+
.step-link:hover { text-decoration: underline; }
252+
:global([data-mode="dark"]) .step-link { color: rgb(var(--color-primary-400)); }
253+
254+
@media (max-width: 1024px) { .steps-grid { grid-template-columns: repeat(3, 1fr); } }
255+
@media (max-width: 768px) { .intro-grid { grid-template-columns: 1fr; gap: 1.5rem; } .steps-grid { grid-template-columns: repeat(2, 1fr); } }
256+
@media (max-width: 640px) { .header-content { flex-direction: column; text-align: center; } .steps-grid { grid-template-columns: 1fr; } }
257+
</style>

0 commit comments

Comments
 (0)