@owlmeans/client-panel 0.1.18-rc.58 → 0.1.18-rc.59
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 +2 -2
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/client-panel/SKILL.md +30 -4
- package/build/auth/login/index.d.ts +2 -0
- package/build/auth/login/index.d.ts.map +1 -1
- package/build/auth/login/index.js +2 -0
- package/build/auth/login/index.js.map +1 -1
- package/build/auth/login/model.d.ts +2 -2
- package/build/auth/login/model.d.ts.map +1 -1
- package/build/auth/login/model.js +5 -2
- package/build/auth/login/model.js.map +1 -1
- package/build/auth/login/provider/consts.local.d.ts +3 -0
- package/build/auth/login/provider/consts.local.d.ts.map +1 -0
- package/build/auth/login/provider/consts.local.js +3 -0
- package/build/auth/login/provider/consts.local.js.map +1 -0
- package/build/auth/login/provider-config.d.ts +37 -0
- package/build/auth/login/provider-config.d.ts.map +1 -0
- package/build/auth/login/provider-config.js +2 -0
- package/build/auth/login/provider-config.js.map +1 -0
- package/build/auth/login/provider.d.ts +5 -0
- package/build/auth/login/provider.d.ts.map +1 -0
- package/build/auth/login/provider.js +55 -0
- package/build/auth/login/provider.js.map +1 -0
- package/build/auth/login/types.d.ts +48 -1
- package/build/auth/login/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/auth/login/index.ts +2 -0
- package/src/auth/login/model.ts +5 -2
- package/src/auth/login/provider/consts.local.ts +2 -0
- package/src/auth/login/provider-config.ts +38 -0
- package/src/auth/login/provider.ts +65 -0
- package/src/auth/login/types.ts +52 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ headless navigation model.
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
bun add @owlmeans/client-panel@^0.1.18-rc.
|
|
19
|
+
bun add @owlmeans/client-panel@^0.1.18-rc.59
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Usage
|
|
@@ -153,7 +153,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
153
153
|
your project's skill store (`.agents/skills/`):
|
|
154
154
|
|
|
155
155
|
```sh
|
|
156
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
156
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.51
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/client-panel",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-10-
|
|
4
|
+
"version": "0.1.18-rc.59",
|
|
5
|
+
"generatedAt": "2026-10-06T11:45:57.680Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/client-panel
|
|
9
9
|
|
|
10
10
|
**Layer:** Client
|
|
11
|
-
**Install:** `"@owlmeans/client-panel": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/client-panel": "^0.1.18-rc.59"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
## Key Exports
|
|
14
14
|
|
|
@@ -40,9 +40,11 @@ controller; apps may override it through their own `buttons` resource.
|
|
|
40
40
|
|
|
41
41
|
## Subpath Exports
|
|
42
42
|
|
|
43
|
-
- `./auth` — `useLoginMethods` and the login model types
|
|
44
|
-
`
|
|
45
|
-
|
|
43
|
+
- `./auth` — `useLoginMethods`, `loginProviderHelper` and the login model types
|
|
44
|
+
(`LoginMethodsModel`, `LoginTermsModel`, `LoginCreditModel`, `LoginProviderConfig`,
|
|
45
|
+
`LoginProviderModel`, `LoginProviderPlacement`, `UseLoginMethodsOptions`). Importing it also
|
|
46
|
+
registers this package's own seven-language `client-panel-auth` bundle and the
|
|
47
|
+
`LoginScreenConfig.provider` augmentation of `@owlmeans/config` by side effect.
|
|
46
48
|
- `./auth/plugins` — the shapes an auth UI plugin is written against: `Ed22519BasicAuthUIPluginForm`
|
|
47
49
|
and its `Ed22519BasicAuthUIPluginFormSchema`.
|
|
48
50
|
|
|
@@ -63,6 +65,30 @@ bound `LoginStep` confirms the terms instead of this screen. `LoginMethodsModel.
|
|
|
63
65
|
(`required && !deferred && !accepted`) — a renderer never blocks on `required`/`accepted` alone, or
|
|
64
66
|
it would re-block a screen whose confirmation moved elsewhere.
|
|
65
67
|
|
|
68
|
+
### The provider disclosure — `LoginMethodsModel.provider`
|
|
69
|
+
|
|
70
|
+
A sign-in screen that a third party runs for an application (a generated target signing people in
|
|
71
|
+
through the platform's IAM) names both parties: who signs the person in, and on behalf of which
|
|
72
|
+
product. The rules live here, headless, so every renderer discloses the same thing:
|
|
73
|
+
|
|
74
|
+
- **Config** — `cfg.security.auth.login.provider?: LoginProviderConfig`
|
|
75
|
+
(`{ name, operator?, info?, placement?: 'top' | 'inline' }`), declared onto `@owlmeans/config`'s
|
|
76
|
+
`LoginScreenConfig` by module augmentation in `auth/login/provider-config.ts` (the
|
|
77
|
+
`client-auth/login/terms-config.ts` pattern), never by editing `@owlmeans/config`. The `./auth`
|
|
78
|
+
barrel re-exports it, so any import of `@owlmeans/client-panel/auth` loads the field; a program
|
|
79
|
+
that builds the literal without one adds `import type {} from '@owlmeans/client-panel/auth'`.
|
|
80
|
+
- **Model** — `loginProviderHelper.resolve(cfg, brand)` → `LoginProviderModel { name, operator,
|
|
81
|
+
product, info: string | null, placement }` or `null`. Null unless the provider `name` AND the
|
|
82
|
+
product (`cfg.brand.name`) are set — a relationship with one party missing is never shown. Blank
|
|
83
|
+
strings are unset (build-time env arrives as `''`); `operator` defaults to `name`; `placement` to
|
|
84
|
+
`'inline'` (anything but `'top'` is inline); `info` survives only as an absolute `http(s)` URL or
|
|
85
|
+
a root-relative path (`//host` and `/\` are refused). `useLoginMethods().provider` carries it.
|
|
86
|
+
- **Copy** — `loginProviderHelper.fill(template, model)` puts `{{provider}}`, `{{operator}}` and
|
|
87
|
+
`{{product}}` into an ALREADY-TRANSLATED sentence in one pass; names never travel as i18next
|
|
88
|
+
params. The keys (`login.provider.*`) and the markup belong to `web-panel`.
|
|
89
|
+
- The disclosure is independent of the credit line: `credit.poweredBy: false` never hides who
|
|
90
|
+
signs a person in.
|
|
91
|
+
|
|
66
92
|
## Usage
|
|
67
93
|
|
|
68
94
|
```typescript
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA"}
|
|
@@ -3,8 +3,8 @@ import type { LoginMethodsModel, UseLoginMethodsOptions } from './types.js';
|
|
|
3
3
|
* Everything a sign-in screen needs, with no opinion about how it looks.
|
|
4
4
|
*
|
|
5
5
|
* Headless and cross-platform, like `usePanelNav`: the rules about what may be offered, what must
|
|
6
|
-
* be confirmed first
|
|
7
|
-
* rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
6
|
+
* be confirmed first, what the credit line says and who is disclosed as signing the person in are
|
|
7
|
+
* the same on every platform, and only the rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
8
8
|
*/
|
|
9
9
|
export declare const useLoginMethods: (opts?: UseLoginMethodsOptions) => LoginMethodsModel;
|
|
10
10
|
//# sourceMappingURL=model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAG3E;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,UAAW,sBAAsB,KAAG,iBAsF/D,CAAA"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState } from 'react';
|
|
2
2
|
import { useContext, useNavigate } from '@owlmeans/client';
|
|
3
3
|
import { resolveCredit, LoginOutcome, LOGIN_SERVICE, loginMethodsHelper, loginTermsHelper } from '@owlmeans/client-auth/login';
|
|
4
|
+
import { loginProviderHelper } from './provider.js';
|
|
4
5
|
/**
|
|
5
6
|
* Everything a sign-in screen needs, with no opinion about how it looks.
|
|
6
7
|
*
|
|
7
8
|
* Headless and cross-platform, like `usePanelNav`: the rules about what may be offered, what must
|
|
8
|
-
* be confirmed first
|
|
9
|
-
* rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
9
|
+
* be confirmed first, what the credit line says and who is disclosed as signing the person in are
|
|
10
|
+
* the same on every platform, and only the rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
10
11
|
*/
|
|
11
12
|
export const useLoginMethods = (opts) => {
|
|
12
13
|
const context = useContext();
|
|
@@ -16,6 +17,7 @@ export const useLoginMethods = (opts) => {
|
|
|
16
17
|
const brand = context.cfg.brand;
|
|
17
18
|
const resolved = useMemo(() => loginTermsHelper.resolveTerms(opts?.terms ?? cfg?.terms), [opts?.terms, cfg]);
|
|
18
19
|
const credit = useMemo(() => resolveCredit(cfg?.credit, brand, context.cfg.service), [cfg, brand, context]);
|
|
20
|
+
const provider = useMemo(() => loginProviderHelper.resolve(cfg?.provider, brand), [cfg, brand]);
|
|
19
21
|
const [accepted, setAccepted] = useState(() => loginTermsHelper.termsAccepted(resolved));
|
|
20
22
|
const [attempted, setAttempted] = useState(false);
|
|
21
23
|
const [busy, setBusy] = useState(null);
|
|
@@ -75,6 +77,7 @@ export const useLoginMethods = (opts) => {
|
|
|
75
77
|
accept,
|
|
76
78
|
},
|
|
77
79
|
credit,
|
|
80
|
+
provider,
|
|
78
81
|
blocked,
|
|
79
82
|
busy,
|
|
80
83
|
outcome,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACtD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../../../../src/auth/login/provider/consts.local.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,eAAO,MAAM,oBAAoB,QAAyC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../../../../src/auth/login/provider/consts.local.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,sCAAsC,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { LoginProviderConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* `LoginScreenConfig.provider`, added by TypeScript module augmentation rather than by editing
|
|
4
|
+
* `@owlmeans/config` itself.
|
|
5
|
+
*
|
|
6
|
+
* `@owlmeans/config` has roughly 85 dependents, and every one of them sits in the same release
|
|
7
|
+
* closure — a config bump ripples through every package that pins a range against it, whether or
|
|
8
|
+
* not it touches sign-in. The field below is read by exactly one consumer family (the sign-in
|
|
9
|
+
* screen's headless model here and its renderers), so it is declared here instead, merged onto the
|
|
10
|
+
* base interface by TypeScript's own declaration merging — the pattern
|
|
11
|
+
* `@owlmeans/client-auth/login`'s `terms-config.ts` set.
|
|
12
|
+
*
|
|
13
|
+
* This file must be part of whatever a consumer's own compilation actually loads, or the merge
|
|
14
|
+
* never happens — an ambient augmentation applies only once the compiler has read the file that
|
|
15
|
+
* declares it. `@owlmeans/client-panel/auth`'s barrel re-exports this module for exactly that
|
|
16
|
+
* reason, so ANY import from `@owlmeans/client-panel/auth` pulls this augmentation into that
|
|
17
|
+
* program. An app that builds its `security.auth.login` literal in a file that imports nothing from
|
|
18
|
+
* it (a config file importing only `@owlmeans/config`) still benefits as long as some other file in
|
|
19
|
+
* the same `tsc` program does; if none does, add `import type {} from '@owlmeans/client-panel/auth'`
|
|
20
|
+
* to the file building the literal — an import with an empty type list, kept only to pull this
|
|
21
|
+
* file into that program's graph.
|
|
22
|
+
*
|
|
23
|
+
* `export {}` below is required, not decorative: without at least one top-level import/export this
|
|
24
|
+
* file is a SCRIPT to TypeScript, not a MODULE, and `declare module '@owlmeans/config'` inside a
|
|
25
|
+
* script augments the global scope instead of merging onto the named module's exports.
|
|
26
|
+
*/
|
|
27
|
+
declare module '@owlmeans/config' {
|
|
28
|
+
interface LoginScreenConfig {
|
|
29
|
+
/**
|
|
30
|
+
* Who signs a person in on this application's behalf — an identity service the application
|
|
31
|
+
* does not itself run. Absent, the screen discloses nothing and renders exactly as before.
|
|
32
|
+
*/
|
|
33
|
+
provider?: LoginProviderConfig;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=provider-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../../../src/auth/login/provider-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,QAAQ,kBAAkB,CAAC;IAChC,UAAU,iBAAiB;QACzB;;;WAGG;QACH,QAAQ,CAAC,EAAE,mBAAmB,CAAA;KAC/B;CACF;AAED,OAAO,EAAG,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-config.js","sourceRoot":"","sources":["../../../src/auth/login/provider-config.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/auth/login/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAuB,mBAAmB,EAAsB,MAAM,YAAY,CAAA;AAE9F,OAAO,sBAAsB,CAAA;AAE7B,eAAO,MAAM,yBAAyB,QAAO,mBAyD5C,CAAA;AAED,eAAO,MAAM,mBAAmB,qBAA8B,CAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { PROVIDER_PLACEHOLDER } from './provider/consts.local.js';
|
|
2
|
+
import './provider-config.js';
|
|
3
|
+
export const createLoginProviderHelper = () => {
|
|
4
|
+
/**
|
|
5
|
+
* A configured value that was actually SET — `''` is how a build-time default spells "unset"
|
|
6
|
+
* (see `resolveCredit` in `@owlmeans/client-auth/login`, which follows the same rule).
|
|
7
|
+
*/
|
|
8
|
+
const set = (value) => typeof value === 'string' && value.trim() !== '' ? value.trim() : null;
|
|
9
|
+
/**
|
|
10
|
+
* A link a person may be sent to: an absolute http(s) URL, or a path on this origin.
|
|
11
|
+
*
|
|
12
|
+
* `//host` is protocol-relative — another origin wearing a path's clothes — and `/\` is read as
|
|
13
|
+
* the same by browsers, so both are refused along with every other scheme (`javascript:` above
|
|
14
|
+
* all, since this lands in an `href`).
|
|
15
|
+
*/
|
|
16
|
+
const link = (value) => {
|
|
17
|
+
const href = set(value);
|
|
18
|
+
if (href == null) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (href.startsWith('/')) {
|
|
22
|
+
return /^\/[/\\]/.test(href) ? null : href;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(href);
|
|
26
|
+
return url.protocol === 'https:' || url.protocol === 'http:' ? href : null;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const resolve = (cfg, brand) => {
|
|
33
|
+
const name = set(cfg?.name);
|
|
34
|
+
const product = set(brand?.name);
|
|
35
|
+
if (name == null || product == null) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
name,
|
|
40
|
+
operator: set(cfg?.operator) ?? name,
|
|
41
|
+
product,
|
|
42
|
+
info: link(cfg?.info),
|
|
43
|
+
placement: cfg?.placement === 'top' ? 'top' : 'inline',
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const fill = (template, model) => {
|
|
47
|
+
const values = {
|
|
48
|
+
provider: model.name, operator: model.operator, product: model.product,
|
|
49
|
+
};
|
|
50
|
+
return template.replace(PROVIDER_PLACEHOLDER, (match, key) => values[key] ?? match);
|
|
51
|
+
};
|
|
52
|
+
return { resolve, fill };
|
|
53
|
+
};
|
|
54
|
+
export const loginProviderHelper = createLoginProviderHelper();
|
|
55
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/auth/login/provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACjE,OAAO,sBAAsB,CAAA;AAE7B,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAwB,EAAE;IACjE;;;OAGG;IACH,MAAM,GAAG,GAAG,CAAC,KAAqB,EAAiB,EAAE,CACnD,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAExE;;;;;;OAMG;IACH,MAAM,IAAI,GAAG,CAAC,KAAqB,EAAiB,EAAE;QACpD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAA;QACvB,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5C,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;YAEzB,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC,CAAA;IAED,MAAM,OAAO,GAAG,CAAC,GAAyB,EAAE,KAAqB,EAA6B,EAAE;QAC9F,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAChC,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;YACpC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO;YACL,IAAI;YACJ,QAAQ,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,IAAI;YACpC,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC;YACrB,SAAS,EAAE,GAAG,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;SACvD,CAAA;IACH,CAAC,CAAA;IAED,MAAM,IAAI,GAAG,CAAC,QAAgB,EAAE,KAAyB,EAAU,EAAE;QACnE,MAAM,MAAM,GAA2B;YACrC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO;SACvE,CAAA;QAED,OAAO,QAAQ,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAA;IAC7F,CAAC,CAAA;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,yBAAyB,EAAE,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LoginMethod, LoginOutcome, ResolvedTermsDocument } from '@owlmeans/client-auth/login';
|
|
2
|
-
import type { LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config';
|
|
2
|
+
import type { BrandSettings, LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config';
|
|
3
3
|
export interface UseLoginMethodsOptions {
|
|
4
4
|
/** Overrides the configuration the context carries. */
|
|
5
5
|
config?: LoginScreenConfig;
|
|
@@ -33,12 +33,59 @@ export interface LoginCreditModel {
|
|
|
33
33
|
poweredBy: boolean;
|
|
34
34
|
line: string | null;
|
|
35
35
|
}
|
|
36
|
+
/** Where the provider disclosure sits: a strip above the card, or a line inside it. */
|
|
37
|
+
export type LoginProviderPlacement = 'top' | 'inline';
|
|
38
|
+
/**
|
|
39
|
+
* Who actually signs a person in, when that is not the application on whose screen they are.
|
|
40
|
+
*
|
|
41
|
+
* Read from `cfg.security.auth.login.provider` (declared onto `LoginScreenConfig` by
|
|
42
|
+
* `./provider-config.ts`). Blank strings are unset, exactly as they are for the credit line: this
|
|
43
|
+
* config is filled from build-time environment variables, and an undelivered one arrives as `''`.
|
|
44
|
+
*/
|
|
45
|
+
export interface LoginProviderConfig {
|
|
46
|
+
/** The sign-in service as a person reads it — "OwlMeans IAM". */
|
|
47
|
+
name: string;
|
|
48
|
+
/** Who runs it. Defaults to `name`. */
|
|
49
|
+
operator?: string;
|
|
50
|
+
/** "More information" — an absolute http(s) URL or a root-relative path; anything else is dropped. */
|
|
51
|
+
info?: string;
|
|
52
|
+
/** Default `'inline'`. */
|
|
53
|
+
placement?: LoginProviderPlacement;
|
|
54
|
+
}
|
|
55
|
+
export interface LoginProviderModel {
|
|
56
|
+
name: string;
|
|
57
|
+
operator: string;
|
|
58
|
+
/** The application the person is signing in to — `cfg.brand.name`. */
|
|
59
|
+
product: string;
|
|
60
|
+
info: string | null;
|
|
61
|
+
placement: LoginProviderPlacement;
|
|
62
|
+
}
|
|
63
|
+
export interface LoginProviderHelper {
|
|
64
|
+
/**
|
|
65
|
+
* The disclosure a sign-in screen renders, or `null` when there is nothing to disclose.
|
|
66
|
+
*
|
|
67
|
+
* Null unless both the provider's `name` and the product (`brand.name`) are set — a sentence
|
|
68
|
+
* relating two parties cannot be written with one of them missing, and a screen that showed half
|
|
69
|
+
* of it would be worse than one that shows none.
|
|
70
|
+
*/
|
|
71
|
+
resolve: (cfg?: LoginProviderConfig, brand?: BrandSettings) => LoginProviderModel | null;
|
|
72
|
+
/**
|
|
73
|
+
* Put the names into a sentence AFTER it was translated.
|
|
74
|
+
*
|
|
75
|
+
* `{{provider}}`, `{{operator}}` and `{{product}}`, replaced in one pass — a name that itself
|
|
76
|
+
* contains a placeholder is never expanded a second time. The names never travel as i18next
|
|
77
|
+
* interpolation parameters, so no translation backend gets the chance to escape or format them.
|
|
78
|
+
*/
|
|
79
|
+
fill: (template: string, model: LoginProviderModel) => string;
|
|
80
|
+
}
|
|
36
81
|
export interface LoginMethodsModel {
|
|
37
82
|
methods: LoginMethod[];
|
|
38
83
|
/** The one a screen highlights and focuses. It is never started automatically. */
|
|
39
84
|
primary: LoginMethod | null;
|
|
40
85
|
terms: LoginTermsModel;
|
|
41
86
|
credit: LoginCreditModel;
|
|
87
|
+
/** Who signs the person in, on behalf of which product — `null` when the app configured nobody. */
|
|
88
|
+
provider: LoginProviderModel | null;
|
|
42
89
|
/** True while the terms have not been confirmed and confirming them is required. */
|
|
43
90
|
blocked: boolean;
|
|
44
91
|
/** Id of the method currently starting. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/auth/login/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnG,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/auth/login/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnG,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE1F,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,KAAK,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAA;IAChC,qDAAqD;IACrD,OAAO,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC,CAAA;CACtE;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAA;IAClB,8FAA8F;IAC9F,SAAS,EAAE,qBAAqB,EAAE,CAAA;IAClC,6FAA6F;IAC7F,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,uFAAuF;AACvF,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,QAAQ,CAAA;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sGAAsG;IACtG,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0BAA0B;IAC1B,SAAS,CAAC,EAAE,sBAAsB,CAAA;CACnC;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,SAAS,EAAE,sBAAsB,CAAA;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,mBAAmB,EAAE,KAAK,CAAC,EAAE,aAAa,KAAK,kBAAkB,GAAG,IAAI,CAAA;IACxF;;;;;;OAMG;IACH,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,MAAM,CAAA;CAC9D;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,kFAAkF;IAClF,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B,KAAK,EAAE,eAAe,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;IACxB,mGAAmG;IACnG,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACnC,oFAAoF;IACpF,OAAO,EAAE,OAAO,CAAA;IAChB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;CACtC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/client-panel",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.59",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -36,16 +36,16 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@hookform/resolvers": "^5.9.1",
|
|
39
|
-
"@owlmeans/client": "^0.1.18-rc.
|
|
40
|
-
"@owlmeans/client-i18n": "^0.1.18-rc.
|
|
41
|
-
"@owlmeans/client-entrypoint": "^0.1.18-rc.
|
|
42
|
-
"@owlmeans/client-route": "^0.1.18-rc.
|
|
39
|
+
"@owlmeans/client": "^0.1.18-rc.52",
|
|
40
|
+
"@owlmeans/client-i18n": "^0.1.18-rc.51",
|
|
41
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.48",
|
|
42
|
+
"@owlmeans/client-route": "^0.1.18-rc.49",
|
|
43
43
|
"@owlmeans/error": "^0.1.18-rc.38",
|
|
44
44
|
"@owlmeans/entrypoint": "^0.1.18-rc.41"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@owlmeans/auth": "^0.1.18-rc.40",
|
|
48
|
-
"@owlmeans/client-auth": "^0.1.18-rc.
|
|
48
|
+
"@owlmeans/client-auth": "^0.1.18-rc.59",
|
|
49
49
|
"@owlmeans/config": "^0.1.18-rc.44",
|
|
50
50
|
"@owlmeans/i18n": "^0.1.18-rc.39",
|
|
51
51
|
"@owlmeans/router": "^0.1.18-rc.33",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@owlmeans/dep-config": "workspace:*",
|
|
66
66
|
"@owlmeans/auth": "^0.1.18-rc.40",
|
|
67
|
-
"@owlmeans/client-auth": "^0.1.18-rc.
|
|
67
|
+
"@owlmeans/client-auth": "^0.1.18-rc.59",
|
|
68
68
|
"@types/react": "^19.3.0",
|
|
69
69
|
"nodemon": "^3.1.14",
|
|
70
70
|
"react-hook-form": "^7.89.0",
|
package/src/auth/login/index.ts
CHANGED
package/src/auth/login/model.ts
CHANGED
|
@@ -4,13 +4,14 @@ import type { CommonConfig } from '@owlmeans/config'
|
|
|
4
4
|
import { resolveCredit, LoginOutcome, LOGIN_SERVICE, loginMethodsHelper, loginTermsHelper } from '@owlmeans/client-auth/login'
|
|
5
5
|
import type { LoginContext, LoginMethod, LoginService } from '@owlmeans/client-auth/login'
|
|
6
6
|
import type { LoginMethodsModel, UseLoginMethodsOptions } from './types.js'
|
|
7
|
+
import { loginProviderHelper } from './provider.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Everything a sign-in screen needs, with no opinion about how it looks.
|
|
10
11
|
*
|
|
11
12
|
* Headless and cross-platform, like `usePanelNav`: the rules about what may be offered, what must
|
|
12
|
-
* be confirmed first
|
|
13
|
-
* rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
13
|
+
* be confirmed first, what the credit line says and who is disclosed as signing the person in are
|
|
14
|
+
* the same on every platform, and only the rendering differs. A renderer that re-derives any of them will drift from the one that does not.
|
|
14
15
|
*/
|
|
15
16
|
export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsModel => {
|
|
16
17
|
const context = useContext() as unknown as LoginContext
|
|
@@ -24,6 +25,7 @@ export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsMode
|
|
|
24
25
|
const credit = useMemo(
|
|
25
26
|
() => resolveCredit(cfg?.credit, brand, context.cfg.service), [cfg, brand, context]
|
|
26
27
|
)
|
|
28
|
+
const provider = useMemo(() => loginProviderHelper.resolve(cfg?.provider, brand), [cfg, brand])
|
|
27
29
|
|
|
28
30
|
const [accepted, setAccepted] = useState(() => loginTermsHelper.termsAccepted(resolved))
|
|
29
31
|
const [attempted, setAttempted] = useState(false)
|
|
@@ -90,6 +92,7 @@ export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsMode
|
|
|
90
92
|
accept,
|
|
91
93
|
},
|
|
92
94
|
credit,
|
|
95
|
+
provider,
|
|
93
96
|
blocked,
|
|
94
97
|
busy,
|
|
95
98
|
outcome,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { LoginProviderConfig } from './types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `LoginScreenConfig.provider`, added by TypeScript module augmentation rather than by editing
|
|
5
|
+
* `@owlmeans/config` itself.
|
|
6
|
+
*
|
|
7
|
+
* `@owlmeans/config` has roughly 85 dependents, and every one of them sits in the same release
|
|
8
|
+
* closure — a config bump ripples through every package that pins a range against it, whether or
|
|
9
|
+
* not it touches sign-in. The field below is read by exactly one consumer family (the sign-in
|
|
10
|
+
* screen's headless model here and its renderers), so it is declared here instead, merged onto the
|
|
11
|
+
* base interface by TypeScript's own declaration merging — the pattern
|
|
12
|
+
* `@owlmeans/client-auth/login`'s `terms-config.ts` set.
|
|
13
|
+
*
|
|
14
|
+
* This file must be part of whatever a consumer's own compilation actually loads, or the merge
|
|
15
|
+
* never happens — an ambient augmentation applies only once the compiler has read the file that
|
|
16
|
+
* declares it. `@owlmeans/client-panel/auth`'s barrel re-exports this module for exactly that
|
|
17
|
+
* reason, so ANY import from `@owlmeans/client-panel/auth` pulls this augmentation into that
|
|
18
|
+
* program. An app that builds its `security.auth.login` literal in a file that imports nothing from
|
|
19
|
+
* it (a config file importing only `@owlmeans/config`) still benefits as long as some other file in
|
|
20
|
+
* the same `tsc` program does; if none does, add `import type {} from '@owlmeans/client-panel/auth'`
|
|
21
|
+
* to the file building the literal — an import with an empty type list, kept only to pull this
|
|
22
|
+
* file into that program's graph.
|
|
23
|
+
*
|
|
24
|
+
* `export {}` below is required, not decorative: without at least one top-level import/export this
|
|
25
|
+
* file is a SCRIPT to TypeScript, not a MODULE, and `declare module '@owlmeans/config'` inside a
|
|
26
|
+
* script augments the global scope instead of merging onto the named module's exports.
|
|
27
|
+
*/
|
|
28
|
+
declare module '@owlmeans/config' {
|
|
29
|
+
interface LoginScreenConfig {
|
|
30
|
+
/**
|
|
31
|
+
* Who signs a person in on this application's behalf — an identity service the application
|
|
32
|
+
* does not itself run. Absent, the screen discloses nothing and renders exactly as before.
|
|
33
|
+
*/
|
|
34
|
+
provider?: LoginProviderConfig
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { BrandSettings } from '@owlmeans/config'
|
|
2
|
+
import type { LoginProviderConfig, LoginProviderHelper, LoginProviderModel } from './types.js'
|
|
3
|
+
import { PROVIDER_PLACEHOLDER } from './provider/consts.local.js'
|
|
4
|
+
import './provider-config.js'
|
|
5
|
+
|
|
6
|
+
export const createLoginProviderHelper = (): LoginProviderHelper => {
|
|
7
|
+
/**
|
|
8
|
+
* A configured value that was actually SET — `''` is how a build-time default spells "unset"
|
|
9
|
+
* (see `resolveCredit` in `@owlmeans/client-auth/login`, which follows the same rule).
|
|
10
|
+
*/
|
|
11
|
+
const set = (value?: string | null): string | null =>
|
|
12
|
+
typeof value === 'string' && value.trim() !== '' ? value.trim() : null
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A link a person may be sent to: an absolute http(s) URL, or a path on this origin.
|
|
16
|
+
*
|
|
17
|
+
* `//host` is protocol-relative — another origin wearing a path's clothes — and `/\` is read as
|
|
18
|
+
* the same by browsers, so both are refused along with every other scheme (`javascript:` above
|
|
19
|
+
* all, since this lands in an `href`).
|
|
20
|
+
*/
|
|
21
|
+
const link = (value?: string | null): string | null => {
|
|
22
|
+
const href = set(value)
|
|
23
|
+
if (href == null) {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
if (href.startsWith('/')) {
|
|
27
|
+
return /^\/[/\\]/.test(href) ? null : href
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const url = new URL(href)
|
|
31
|
+
|
|
32
|
+
return url.protocol === 'https:' || url.protocol === 'http:' ? href : null
|
|
33
|
+
} catch {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const resolve = (cfg?: LoginProviderConfig, brand?: BrandSettings): LoginProviderModel | null => {
|
|
39
|
+
const name = set(cfg?.name)
|
|
40
|
+
const product = set(brand?.name)
|
|
41
|
+
if (name == null || product == null) {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
operator: set(cfg?.operator) ?? name,
|
|
48
|
+
product,
|
|
49
|
+
info: link(cfg?.info),
|
|
50
|
+
placement: cfg?.placement === 'top' ? 'top' : 'inline',
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const fill = (template: string, model: LoginProviderModel): string => {
|
|
55
|
+
const values: Record<string, string> = {
|
|
56
|
+
provider: model.name, operator: model.operator, product: model.product,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return template.replace(PROVIDER_PLACEHOLDER, (match, key: string) => values[key] ?? match)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { resolve, fill }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const loginProviderHelper = createLoginProviderHelper()
|
package/src/auth/login/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LoginMethod, LoginOutcome, ResolvedTermsDocument } from '@owlmeans/client-auth/login'
|
|
2
|
-
import type { LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config'
|
|
2
|
+
import type { BrandSettings, LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config'
|
|
3
3
|
|
|
4
4
|
export interface UseLoginMethodsOptions {
|
|
5
5
|
/** Overrides the configuration the context carries. */
|
|
@@ -37,12 +37,63 @@ export interface LoginCreditModel {
|
|
|
37
37
|
line: string | null
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/** Where the provider disclosure sits: a strip above the card, or a line inside it. */
|
|
41
|
+
export type LoginProviderPlacement = 'top' | 'inline'
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Who actually signs a person in, when that is not the application on whose screen they are.
|
|
45
|
+
*
|
|
46
|
+
* Read from `cfg.security.auth.login.provider` (declared onto `LoginScreenConfig` by
|
|
47
|
+
* `./provider-config.ts`). Blank strings are unset, exactly as they are for the credit line: this
|
|
48
|
+
* config is filled from build-time environment variables, and an undelivered one arrives as `''`.
|
|
49
|
+
*/
|
|
50
|
+
export interface LoginProviderConfig {
|
|
51
|
+
/** The sign-in service as a person reads it — "OwlMeans IAM". */
|
|
52
|
+
name: string
|
|
53
|
+
/** Who runs it. Defaults to `name`. */
|
|
54
|
+
operator?: string
|
|
55
|
+
/** "More information" — an absolute http(s) URL or a root-relative path; anything else is dropped. */
|
|
56
|
+
info?: string
|
|
57
|
+
/** Default `'inline'`. */
|
|
58
|
+
placement?: LoginProviderPlacement
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface LoginProviderModel {
|
|
62
|
+
name: string
|
|
63
|
+
operator: string
|
|
64
|
+
/** The application the person is signing in to — `cfg.brand.name`. */
|
|
65
|
+
product: string
|
|
66
|
+
info: string | null
|
|
67
|
+
placement: LoginProviderPlacement
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface LoginProviderHelper {
|
|
71
|
+
/**
|
|
72
|
+
* The disclosure a sign-in screen renders, or `null` when there is nothing to disclose.
|
|
73
|
+
*
|
|
74
|
+
* Null unless both the provider's `name` and the product (`brand.name`) are set — a sentence
|
|
75
|
+
* relating two parties cannot be written with one of them missing, and a screen that showed half
|
|
76
|
+
* of it would be worse than one that shows none.
|
|
77
|
+
*/
|
|
78
|
+
resolve: (cfg?: LoginProviderConfig, brand?: BrandSettings) => LoginProviderModel | null
|
|
79
|
+
/**
|
|
80
|
+
* Put the names into a sentence AFTER it was translated.
|
|
81
|
+
*
|
|
82
|
+
* `{{provider}}`, `{{operator}}` and `{{product}}`, replaced in one pass — a name that itself
|
|
83
|
+
* contains a placeholder is never expanded a second time. The names never travel as i18next
|
|
84
|
+
* interpolation parameters, so no translation backend gets the chance to escape or format them.
|
|
85
|
+
*/
|
|
86
|
+
fill: (template: string, model: LoginProviderModel) => string
|
|
87
|
+
}
|
|
88
|
+
|
|
40
89
|
export interface LoginMethodsModel {
|
|
41
90
|
methods: LoginMethod[]
|
|
42
91
|
/** The one a screen highlights and focuses. It is never started automatically. */
|
|
43
92
|
primary: LoginMethod | null
|
|
44
93
|
terms: LoginTermsModel
|
|
45
94
|
credit: LoginCreditModel
|
|
95
|
+
/** Who signs the person in, on behalf of which product — `null` when the app configured nobody. */
|
|
96
|
+
provider: LoginProviderModel | null
|
|
46
97
|
/** True while the terms have not been confirmed and confirming them is required. */
|
|
47
98
|
blocked: boolean
|
|
48
99
|
/** Id of the method currently starting. */
|