@owlmeans/web-client 0.1.18-rc.2 → 0.1.18-rc.20
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/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/web-client/SKILL.md +45 -10
- package/build/components/dispatcher/component.d.ts.map +1 -1
- package/build/components/dispatcher/component.js +54 -9
- package/build/components/dispatcher/component.js.map +1 -1
- package/build/context.d.ts.map +1 -1
- package/build/context.js +2 -0
- package/build/context.js.map +1 -1
- package/build/exports.d.ts +1 -0
- package/build/exports.d.ts.map +1 -1
- package/build/i18n/auth-be.json +32 -0
- package/build/i18n/auth-de.json +32 -0
- package/build/i18n/auth-en.json +32 -0
- package/build/i18n/auth-es.json +32 -0
- package/build/i18n/auth-pl.json +32 -0
- package/build/i18n/auth-ru.json +32 -0
- package/build/i18n/auth-uk.json +32 -0
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/login/consts.d.ts +11 -0
- package/build/login/consts.d.ts.map +1 -0
- package/build/login/consts.js +11 -0
- package/build/login/consts.js.map +1 -0
- package/build/login/index.d.ts +8 -0
- package/build/login/index.d.ts.map +1 -0
- package/build/login/index.js +8 -0
- package/build/login/index.js.map +1 -0
- package/build/login/pump.d.ts +20 -0
- package/build/login/pump.d.ts.map +1 -0
- package/build/login/pump.js +46 -0
- package/build/login/pump.js.map +1 -0
- package/build/login/redirect.d.ts +9 -0
- package/build/login/redirect.d.ts.map +1 -0
- package/build/login/redirect.js +47 -0
- package/build/login/redirect.js.map +1 -0
- package/build/login/screen.d.ts +15 -0
- package/build/login/screen.d.ts.map +1 -0
- package/build/login/screen.js +97 -0
- package/build/login/screen.js.map +1 -0
- package/build/login/service.d.ts +10 -0
- package/build/login/service.d.ts.map +1 -0
- package/build/login/service.js +17 -0
- package/build/login/service.js.map +1 -0
- package/build/login/surrogate.d.ts +21 -0
- package/build/login/surrogate.d.ts.map +1 -0
- package/build/login/surrogate.js +144 -0
- package/build/login/surrogate.js.map +1 -0
- package/build/login/view.d.ts +26 -0
- package/build/login/view.d.ts.map +1 -0
- package/build/login/view.js +73 -0
- package/build/login/view.js.map +1 -0
- package/build/main.d.ts +9 -1
- package/build/main.d.ts.map +1 -1
- package/build/main.js +9 -1
- package/build/main.js.map +1 -1
- package/build/modules.d.ts.map +1 -1
- package/build/modules.js +11 -1
- package/build/modules.js.map +1 -1
- package/build/types.d.ts +2 -1
- package/build/types.d.ts.map +1 -1
- package/package.json +17 -17
- package/src/components/dispatcher/component.tsx +72 -8
- package/src/context.ts +2 -0
- package/src/exports.ts +1 -0
- package/src/i18n/auth-be.json +32 -0
- package/src/i18n/auth-de.json +32 -0
- package/src/i18n/auth-en.json +32 -0
- package/src/i18n/auth-es.json +32 -0
- package/src/i18n/auth-pl.json +32 -0
- package/src/i18n/auth-ru.json +32 -0
- package/src/i18n/auth-uk.json +32 -0
- package/src/index.ts +1 -0
- package/src/login/consts.ts +12 -0
- package/src/login/index.ts +7 -0
- package/src/login/pump.ts +53 -0
- package/src/login/redirect.ts +59 -0
- package/src/login/screen.tsx +119 -0
- package/src/login/service.ts +19 -0
- package/src/login/surrogate.ts +175 -0
- package/src/login/view.tsx +94 -0
- package/src/main.tsx +11 -2
- package/src/modules.ts +12 -1
- package/src/types.ts +2 -1
package/build/main.js
CHANGED
|
@@ -21,5 +21,13 @@ export const render = (node, opts) => {
|
|
|
21
21
|
};
|
|
22
22
|
opts?.onReady ?? true ? window.addEventListener('DOMContentLoaded', _callback) : _callback();
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Mount the application.
|
|
26
|
+
*
|
|
27
|
+
* `children` are rendered inside the context provider and BEFORE the router, which makes them the
|
|
28
|
+
* app's global overlay slot: a consent dialog, a toast surface, anything that must exist on every
|
|
29
|
+
* route and outlive navigation. A component mounted inside a route instead is torn down and
|
|
30
|
+
* rebuilt on every navigation, which for a dialog means it closes itself.
|
|
31
|
+
*/
|
|
32
|
+
export const renderApp = (context, opts, children) => render(_jsx(WebApp, { context: context, children: children }), opts);
|
|
25
33
|
//# sourceMappingURL=main.js.map
|
package/build/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAE9C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAe,EAAE,IAAoB,EAAE,EAAE;IAC9D,MAAM,SAAS,GAAG,GAAG,EAAE;QACrB,MAAM,GAAG,GAAG,IAAI,EAAE,KAAK,IAAI,YAAY,CAAA;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAA;QACzC,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAA;QAC7C,CAAC;QACD,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAA;QAC3D,CAAC;QACD,IAAI,IAAI,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;YAC3B,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACzB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC,CAAA;IAED,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAA;AAC9F,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAE9C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAe,EAAE,IAAoB,EAAE,EAAE;IAC9D,MAAM,SAAS,GAAG,GAAG,EAAE;QACrB,MAAM,GAAG,GAAG,IAAI,EAAE,KAAK,IAAI,YAAY,CAAA;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAA;QACzC,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAA;QAC7C,CAAC;QACD,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAA;QAC3D,CAAC;QACD,IAAI,IAAI,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;YAC3B,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACzB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC,CAAA;IAED,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAA;AAC9F,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,OAAU,EAAE,IAAoB,EAAE,QAAoB,EACtD,EAAE,CAAC,MAAM,CAAC,KAAC,MAAM,IAAC,OAAO,EAAE,OAAO,YAAG,QAAQ,GAAU,EAAE,IAAI,CAAC,CAAA"}
|
package/build/modules.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modules.d.ts","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"modules.d.ts","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,OAAO,uHAAO,CAAA"}
|
package/build/modules.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import { DISPATCHER } from '@owlmeans/auth';
|
|
1
|
+
import { DISPATCHER, DISPATCHER_SURROGATE } from '@owlmeans/auth';
|
|
2
2
|
import { modules as list } from '@owlmeans/client-auth';
|
|
3
3
|
import { elevate } from '@owlmeans/client-entrypoint';
|
|
4
4
|
import { handler } from '@owlmeans/client';
|
|
5
5
|
import { Dispatcher } from './components/dispatcher/component.js';
|
|
6
|
+
import { SurrogateScreen } from './login/screen.js';
|
|
6
7
|
elevate(list, DISPATCHER, handler(Dispatcher), { force: true });
|
|
8
|
+
/**
|
|
9
|
+
* Elevated here rather than in a relying-party package on purpose.
|
|
10
|
+
*
|
|
11
|
+
* The surrogate mechanic is not OIDC-specific — it is "run the login route one window up and hand
|
|
12
|
+
* the result back" — and every web application already spreads this list. Putting the screen here
|
|
13
|
+
* means `web-panel`, `mui-panel` and every generated target gain a working login window with no
|
|
14
|
+
* edit of their own, which is the only way an application that is already deployed can get it.
|
|
15
|
+
*/
|
|
16
|
+
elevate(list, DISPATCHER_SURROGATE, handler(SurrogateScreen));
|
|
7
17
|
export const modules = list;
|
|
8
18
|
//# sourceMappingURL=modules.js.map
|
package/build/modules.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modules.js","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"modules.js","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACjE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AAE/D;;;;;;;GAOG;AACH,OAAO,CAAC,IAAI,EAAE,oBAAoB,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAA;AAE7D,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAA"}
|
package/build/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AuthServiceAppend } from '@owlmeans/client-auth';
|
|
2
|
+
import type { LoginServiceAppend } from '@owlmeans/client-auth/login';
|
|
2
3
|
import type { ClientConfig } from '@owlmeans/client-context';
|
|
3
4
|
import type { ClientContext } from '@owlmeans/client';
|
|
4
5
|
export interface RenderOptions {
|
|
@@ -9,6 +10,6 @@ export interface RenderOptions {
|
|
|
9
10
|
}
|
|
10
11
|
export interface AppConfig extends ClientConfig {
|
|
11
12
|
}
|
|
12
|
-
export interface AppContext<C extends AppConfig = AppConfig> extends ClientContext<C>, AuthServiceAppend {
|
|
13
|
+
export interface AppContext<C extends AppConfig = AppConfig> extends ClientContext<C>, AuthServiceAppend, LoginServiceAppend {
|
|
13
14
|
}
|
|
14
15
|
//# sourceMappingURL=types.d.ts.map
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,SAAU,SAAQ,YAAY;CAC9C;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC,EACnF,iBAAiB;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,SAAU,SAAQ,YAAY;CAC9C;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC,EACnF,iBAAiB,EAAE,kBAAkB;CACtC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/web-client",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.20",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -21,22 +21,22 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
25
|
-
"@owlmeans/auth-common": "^0.1.18-rc.
|
|
26
|
-
"@owlmeans/client": "^0.1.18-rc.
|
|
27
|
-
"@owlmeans/client-auth": "^0.1.18-rc.
|
|
28
|
-
"@owlmeans/client-context": "^0.1.18-rc.
|
|
29
|
-
"@owlmeans/client-i18n": "^0.1.18-rc.
|
|
30
|
-
"@owlmeans/client-entrypoint": "^0.1.18-rc.
|
|
31
|
-
"@owlmeans/client-resource": "^0.1.18-rc.
|
|
32
|
-
"@owlmeans/client-route": "^0.1.18-rc.
|
|
33
|
-
"@owlmeans/config": "^0.1.18-rc.
|
|
34
|
-
"@owlmeans/context": "^0.1.18-rc.
|
|
35
|
-
"@owlmeans/error": "^0.1.18-rc.
|
|
36
|
-
"@owlmeans/i18n": "^0.1.18-rc.
|
|
37
|
-
"@owlmeans/route": "^0.1.18-rc.
|
|
38
|
-
"@owlmeans/web-db": "^0.1.18-rc.
|
|
39
|
-
"@owlmeans/web-router": "^0.1.18-rc.
|
|
24
|
+
"@owlmeans/auth": "^0.1.18-rc.7",
|
|
25
|
+
"@owlmeans/auth-common": "^0.1.18-rc.9",
|
|
26
|
+
"@owlmeans/client": "^0.1.18-rc.11",
|
|
27
|
+
"@owlmeans/client-auth": "^0.1.18-rc.13",
|
|
28
|
+
"@owlmeans/client-context": "^0.1.18-rc.9",
|
|
29
|
+
"@owlmeans/client-i18n": "^0.1.18-rc.10",
|
|
30
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.9",
|
|
31
|
+
"@owlmeans/client-resource": "^0.1.18-rc.9",
|
|
32
|
+
"@owlmeans/client-route": "^0.1.18-rc.9",
|
|
33
|
+
"@owlmeans/config": "^0.1.18-rc.8",
|
|
34
|
+
"@owlmeans/context": "^0.1.18-rc.6",
|
|
35
|
+
"@owlmeans/error": "^0.1.18-rc.6",
|
|
36
|
+
"@owlmeans/i18n": "^0.1.18-rc.6",
|
|
37
|
+
"@owlmeans/route": "^0.1.18-rc.6",
|
|
38
|
+
"@owlmeans/web-db": "^0.1.18-rc.10",
|
|
39
|
+
"@owlmeans/web-router": "^0.1.18-rc.11",
|
|
40
40
|
"react-dropzone": "^14.3.5"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
import { DispatcherHOC } from '@owlmeans/client-auth'
|
|
2
|
-
import { useEffect } from 'react'
|
|
2
|
+
import { useEffect, useState } from 'react'
|
|
3
3
|
import { useI18nLib } from '@owlmeans/client-i18n'
|
|
4
4
|
import { AUTH_QUERY } from '@owlmeans/auth'
|
|
5
|
+
import {
|
|
6
|
+
FallbackLoginScreen, LoginIntent, LoginOutcome, ResumeAction, resumeAction,
|
|
7
|
+
} from '@owlmeans/client-auth/login'
|
|
8
|
+
import { LoginSurrogateView, SurrogateStage } from '../../login/view.js'
|
|
5
9
|
import { useContext } from '../../context.js'
|
|
6
10
|
|
|
7
|
-
export const Dispatcher = DispatcherHOC(({ provideToken }) => {
|
|
11
|
+
export const Dispatcher = DispatcherHOC(({ provideToken, navigate }) => {
|
|
8
12
|
const context = useContext()
|
|
9
13
|
const [query] = context.router().useSearchParams()
|
|
10
14
|
|
|
11
15
|
const t = useI18nLib('auth', 'dispatcher')
|
|
16
|
+
const tAuth = useI18nLib('auth')
|
|
17
|
+
|
|
18
|
+
// Read in the component body, not in the effect: `window.name` is set by `window.open` before
|
|
19
|
+
// this document loads and `sessionStorage` carries the marker across the provider round trip, so
|
|
20
|
+
// the answer is already correct at first paint — and a first paint that showed the application
|
|
21
|
+
// before an effect corrected it is the defect this exists to prevent.
|
|
22
|
+
const [env] = useState(() => context.login().env())
|
|
23
|
+
const [outcome, setOutcome] = useState<LoginOutcome | null>(null)
|
|
24
|
+
// Nothing to return from and nobody signed in: offer the choice rather than starting a flow.
|
|
25
|
+
const [choose, setChoose] = useState(false)
|
|
12
26
|
|
|
13
27
|
useEffect(() => {
|
|
28
|
+
// First statement of the effect on purpose: everything below can navigate this window away.
|
|
29
|
+
context.login().enter()
|
|
30
|
+
|
|
14
31
|
const token = query.get(AUTH_QUERY)
|
|
15
32
|
if (token != null) {
|
|
16
33
|
const params: Record<string, string> = {}
|
|
@@ -21,15 +38,62 @@ export const Dispatcher = DispatcherHOC(({ provideToken }) => {
|
|
|
21
38
|
})
|
|
22
39
|
|
|
23
40
|
provideToken({ token }, params)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (authzToken == null) {
|
|
27
|
-
provideToken({ token: '' }, undefined)
|
|
28
|
-
}
|
|
29
|
-
})
|
|
41
|
+
|
|
42
|
+
return
|
|
30
43
|
}
|
|
44
|
+
|
|
45
|
+
void context.auth().authenticated().then(async authzToken => {
|
|
46
|
+
if (authzToken == null || authzToken === '') {
|
|
47
|
+
setChoose(true)
|
|
48
|
+
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
// A session already exists in THIS document. Whether it is useful here, or belongs to the
|
|
52
|
+
// window that opened this one, is the plugin's call — the dispatcher reads no environment.
|
|
53
|
+
const settled = await context.login().resume(authzToken)
|
|
54
|
+
switch (resumeAction(settled)) {
|
|
55
|
+
case ResumeAction.Stop:
|
|
56
|
+
return
|
|
57
|
+
case ResumeAction.Render:
|
|
58
|
+
setOutcome(settled)
|
|
59
|
+
|
|
60
|
+
return
|
|
61
|
+
default:
|
|
62
|
+
await navigate()
|
|
63
|
+
}
|
|
64
|
+
})
|
|
31
65
|
}, [])
|
|
32
66
|
|
|
67
|
+
// A surrogate window never renders the application, whatever else is true. Checked ahead of
|
|
68
|
+
// every other return for exactly that reason.
|
|
69
|
+
//
|
|
70
|
+
// `choose` is the one state this must NOT mask, and it is not an exception to the rule: the
|
|
71
|
+
// login chooser is not the application. A login window with nothing to return from and nobody
|
|
72
|
+
// signed in has exactly one thing left that can move it forward, and that is asking which
|
|
73
|
+
// provider. The surrogate URL normally carries `?method=` so the question is already settled,
|
|
74
|
+
// but a window opened from a bare "Log in" — the framed application's own header button — has no
|
|
75
|
+
// method to send. Returning the "working" panel there left the popup on "Signing you in…"
|
|
76
|
+
// permanently, because nothing was ever going to start.
|
|
77
|
+
if (env.surrogate && !choose) {
|
|
78
|
+
return <LoginSurrogateView
|
|
79
|
+
intent={LoginIntent.Login}
|
|
80
|
+
stage={
|
|
81
|
+
outcome === LoginOutcome.Orphaned ? SurrogateStage.Orphaned
|
|
82
|
+
: outcome === LoginOutcome.Failed ? SurrogateStage.Failed
|
|
83
|
+
: outcome === LoginOutcome.Gesture ? SurrogateStage.Gesture
|
|
84
|
+
: SurrogateStage.Working
|
|
85
|
+
}
|
|
86
|
+
onAction={() => window.close()}
|
|
87
|
+
translate={(key, defaultValue) => tAuth(key, { defaultValue })}
|
|
88
|
+
/>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (choose) {
|
|
92
|
+
const Screen = context.login().screen() ?? FallbackLoginScreen
|
|
93
|
+
|
|
94
|
+
return <Screen translate={(key, defaultValue) => tAuth(key, { defaultValue })} />
|
|
95
|
+
}
|
|
96
|
+
|
|
33
97
|
return query.has(AUTH_QUERY) ? <div style={{
|
|
34
98
|
width: '100%', // Makes the div occupy the full width
|
|
35
99
|
display: 'flex', // Enables flexbox layout
|
package/src/context.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { useContext as useCtx } from '@owlmeans/client'
|
|
|
8
8
|
import { extractPrimaryHost } from './helper.js'
|
|
9
9
|
import { appendWebAuthService } from './service.js'
|
|
10
10
|
import { appendWebRouter } from '@owlmeans/web-router'
|
|
11
|
+
import { appendWebLogin } from './login/service.js'
|
|
11
12
|
|
|
12
13
|
export const makeContext = <C extends AppConfig = AppConfig, T extends AppContext<C> = AppContext<C>>(
|
|
13
14
|
cfg: C
|
|
@@ -19,6 +20,7 @@ export const makeContext = <C extends AppConfig = AppConfig, T extends AppContex
|
|
|
19
20
|
appendWebDbService<C, T>(context)
|
|
20
21
|
appendClientResource<C, T>(context, AUTH_RESOURCE)
|
|
21
22
|
appendWebRouter<C, T>(context)
|
|
23
|
+
appendWebLogin<C, T>(context)
|
|
22
24
|
|
|
23
25
|
context.makeContext = makeContext as typeof context.makeContext
|
|
24
26
|
|
package/src/exports.ts
CHANGED
|
@@ -5,5 +5,6 @@ export { service } from '@owlmeans/config'
|
|
|
5
5
|
export { AppType, HOME, ROOT, BASE, GUEST } from '@owlmeans/context'
|
|
6
6
|
export { entrypoint, elevate, provideRequest, stab } from '@owlmeans/client-entrypoint'
|
|
7
7
|
export { route, frontend } from '@owlmeans/route'
|
|
8
|
+
export type { ClientEntrypoint } from '@owlmeans/client-entrypoint'
|
|
8
9
|
export type { ClientEntrypoint as Module } from '@owlmeans/client-entrypoint'
|
|
9
10
|
export type { ClientRoute as Route } from '@owlmeans/client-route'
|
package/src/i18n/auth-be.json
CHANGED
|
@@ -2,5 +2,37 @@
|
|
|
2
2
|
"dispatcher": {
|
|
3
3
|
"loading": "Загрузка...",
|
|
4
4
|
"error": "Не ўдалося прайсці аўтэнтыфікацыю: {{error}}"
|
|
5
|
+
},
|
|
6
|
+
"surrogate": {
|
|
7
|
+
"login": {
|
|
8
|
+
"working": "Выконваем уваход…",
|
|
9
|
+
"gesture": {
|
|
10
|
+
"title": "Працягніце, каб увайсці.",
|
|
11
|
+
"action": "Увайсці"
|
|
12
|
+
},
|
|
13
|
+
"handing": "Гатова. Вяртаемся ў праграму…",
|
|
14
|
+
"orphaned": "Уваход выкананы. Можаце закрыць гэтае акно і вярнуцца ў праграму.",
|
|
15
|
+
"failed": "Не ўдалося завяршыць. Закрыйце гэтае акно і паспрабуйце зноў.",
|
|
16
|
+
"standalone": {
|
|
17
|
+
"title": "Гэтая старонка выкарыстоўваецца для ўваходу з іншага акна.",
|
|
18
|
+
"action": "Адкрыць праграму"
|
|
19
|
+
},
|
|
20
|
+
"close": "Закрыць"
|
|
21
|
+
},
|
|
22
|
+
"logout": {
|
|
23
|
+
"working": "Выконваем выхад…",
|
|
24
|
+
"gesture": {
|
|
25
|
+
"title": "Працягніце, каб выйсці.",
|
|
26
|
+
"action": "Выйсці"
|
|
27
|
+
},
|
|
28
|
+
"handing": "Выхад выкананы. Вяртаемся ў праграму…",
|
|
29
|
+
"orphaned": "Выхад выкананы. Можаце закрыць гэтае акно.",
|
|
30
|
+
"failed": "Выхад тут не завершаны. Можаце закрыць гэтае акно.",
|
|
31
|
+
"standalone": {
|
|
32
|
+
"title": "Гэтая старонка выкарыстоўваецца для выхаду з іншага акна.",
|
|
33
|
+
"action": "Адкрыць праграму"
|
|
34
|
+
},
|
|
35
|
+
"close": "Закрыць"
|
|
36
|
+
}
|
|
5
37
|
}
|
|
6
38
|
}
|
package/src/i18n/auth-de.json
CHANGED
|
@@ -2,5 +2,37 @@
|
|
|
2
2
|
"dispatcher": {
|
|
3
3
|
"loading": "Wird geladen...",
|
|
4
4
|
"error": "Authentifizierung fehlgeschlagen: {{error}}"
|
|
5
|
+
},
|
|
6
|
+
"surrogate": {
|
|
7
|
+
"login": {
|
|
8
|
+
"working": "Anmeldung läuft…",
|
|
9
|
+
"gesture": {
|
|
10
|
+
"title": "Fortfahren, um sich anzumelden.",
|
|
11
|
+
"action": "Anmelden"
|
|
12
|
+
},
|
|
13
|
+
"handing": "Fertig. Zurück zur Anwendung…",
|
|
14
|
+
"orphaned": "Angemeldet. Sie können dieses Fenster schließen und in der Anwendung fortfahren.",
|
|
15
|
+
"failed": "Das wurde nicht abgeschlossen. Schließen Sie dieses Fenster und versuchen Sie es erneut.",
|
|
16
|
+
"standalone": {
|
|
17
|
+
"title": "Diese Seite dient der Anmeldung aus einem anderen Fenster.",
|
|
18
|
+
"action": "Anwendung öffnen"
|
|
19
|
+
},
|
|
20
|
+
"close": "Schließen"
|
|
21
|
+
},
|
|
22
|
+
"logout": {
|
|
23
|
+
"working": "Abmeldung läuft…",
|
|
24
|
+
"gesture": {
|
|
25
|
+
"title": "Fortfahren, um sich abzumelden.",
|
|
26
|
+
"action": "Abmelden"
|
|
27
|
+
},
|
|
28
|
+
"handing": "Abgemeldet. Zurück zur Anwendung…",
|
|
29
|
+
"orphaned": "Abgemeldet. Sie können dieses Fenster schließen.",
|
|
30
|
+
"failed": "Die Abmeldung wurde hier nicht abgeschlossen. Sie können dieses Fenster schließen.",
|
|
31
|
+
"standalone": {
|
|
32
|
+
"title": "Diese Seite dient der Abmeldung aus einem anderen Fenster.",
|
|
33
|
+
"action": "Anwendung öffnen"
|
|
34
|
+
},
|
|
35
|
+
"close": "Schließen"
|
|
36
|
+
}
|
|
5
37
|
}
|
|
6
38
|
}
|
package/src/i18n/auth-en.json
CHANGED
|
@@ -2,5 +2,37 @@
|
|
|
2
2
|
"dispatcher": {
|
|
3
3
|
"loading": "Loading...",
|
|
4
4
|
"error": "Authentication failed: {{error}}"
|
|
5
|
+
},
|
|
6
|
+
"surrogate": {
|
|
7
|
+
"login": {
|
|
8
|
+
"working": "Signing you in…",
|
|
9
|
+
"gesture": {
|
|
10
|
+
"title": "Continue to sign in.",
|
|
11
|
+
"action": "Sign in"
|
|
12
|
+
},
|
|
13
|
+
"handing": "Done. Returning to the application…",
|
|
14
|
+
"orphaned": "Signed in. You can close this window and continue in the application.",
|
|
15
|
+
"failed": "That did not complete. You can close this window and try again.",
|
|
16
|
+
"standalone": {
|
|
17
|
+
"title": "This page is used to sign in from another window.",
|
|
18
|
+
"action": "Open the application"
|
|
19
|
+
},
|
|
20
|
+
"close": "Close"
|
|
21
|
+
},
|
|
22
|
+
"logout": {
|
|
23
|
+
"working": "Signing you out…",
|
|
24
|
+
"gesture": {
|
|
25
|
+
"title": "Continue to sign out.",
|
|
26
|
+
"action": "Sign out"
|
|
27
|
+
},
|
|
28
|
+
"handing": "Signed out. Returning to the application…",
|
|
29
|
+
"orphaned": "Signed out. You can close this window.",
|
|
30
|
+
"failed": "Sign-out did not complete here. You can close this window.",
|
|
31
|
+
"standalone": {
|
|
32
|
+
"title": "This page is used to sign out from another window.",
|
|
33
|
+
"action": "Open the application"
|
|
34
|
+
},
|
|
35
|
+
"close": "Close"
|
|
36
|
+
}
|
|
5
37
|
}
|
|
6
38
|
}
|
package/src/i18n/auth-es.json
CHANGED
|
@@ -2,5 +2,37 @@
|
|
|
2
2
|
"dispatcher": {
|
|
3
3
|
"loading": "Cargando...",
|
|
4
4
|
"error": "Error de autenticación: {{error}}"
|
|
5
|
+
},
|
|
6
|
+
"surrogate": {
|
|
7
|
+
"login": {
|
|
8
|
+
"working": "Iniciando sesión…",
|
|
9
|
+
"gesture": {
|
|
10
|
+
"title": "Continúa para iniciar sesión.",
|
|
11
|
+
"action": "Iniciar sesión"
|
|
12
|
+
},
|
|
13
|
+
"handing": "Listo. Volviendo a la aplicación…",
|
|
14
|
+
"orphaned": "Sesión iniciada. Puedes cerrar esta ventana y continuar en la aplicación.",
|
|
15
|
+
"failed": "No se completó. Cierra esta ventana e inténtalo de nuevo.",
|
|
16
|
+
"standalone": {
|
|
17
|
+
"title": "Esta página se usa para iniciar sesión desde otra ventana.",
|
|
18
|
+
"action": "Abrir la aplicación"
|
|
19
|
+
},
|
|
20
|
+
"close": "Cerrar"
|
|
21
|
+
},
|
|
22
|
+
"logout": {
|
|
23
|
+
"working": "Cerrando sesión…",
|
|
24
|
+
"gesture": {
|
|
25
|
+
"title": "Continúa para cerrar sesión.",
|
|
26
|
+
"action": "Cerrar sesión"
|
|
27
|
+
},
|
|
28
|
+
"handing": "Sesión cerrada. Volviendo a la aplicación…",
|
|
29
|
+
"orphaned": "Sesión cerrada. Puedes cerrar esta ventana.",
|
|
30
|
+
"failed": "El cierre de sesión no se completó aquí. Puedes cerrar esta ventana.",
|
|
31
|
+
"standalone": {
|
|
32
|
+
"title": "Esta página se usa para cerrar sesión desde otra ventana.",
|
|
33
|
+
"action": "Abrir la aplicación"
|
|
34
|
+
},
|
|
35
|
+
"close": "Cerrar"
|
|
36
|
+
}
|
|
5
37
|
}
|
|
6
38
|
}
|
package/src/i18n/auth-pl.json
CHANGED
|
@@ -2,5 +2,37 @@
|
|
|
2
2
|
"dispatcher": {
|
|
3
3
|
"loading": "Ładowanie...",
|
|
4
4
|
"error": "Uwierzytelnianie nie powiodło się: {{error}}"
|
|
5
|
+
},
|
|
6
|
+
"surrogate": {
|
|
7
|
+
"login": {
|
|
8
|
+
"working": "Trwa logowanie…",
|
|
9
|
+
"gesture": {
|
|
10
|
+
"title": "Kontynuuj, aby się zalogować.",
|
|
11
|
+
"action": "Zaloguj się"
|
|
12
|
+
},
|
|
13
|
+
"handing": "Gotowe. Wracamy do aplikacji…",
|
|
14
|
+
"orphaned": "Zalogowano. Możesz zamknąć to okno i kontynuować w aplikacji.",
|
|
15
|
+
"failed": "Nie udało się ukończyć. Zamknij to okno i spróbuj ponownie.",
|
|
16
|
+
"standalone": {
|
|
17
|
+
"title": "Ta strona służy do logowania z innego okna.",
|
|
18
|
+
"action": "Otwórz aplikację"
|
|
19
|
+
},
|
|
20
|
+
"close": "Zamknij"
|
|
21
|
+
},
|
|
22
|
+
"logout": {
|
|
23
|
+
"working": "Trwa wylogowywanie…",
|
|
24
|
+
"gesture": {
|
|
25
|
+
"title": "Kontynuuj, aby się wylogować.",
|
|
26
|
+
"action": "Wyloguj się"
|
|
27
|
+
},
|
|
28
|
+
"handing": "Wylogowano. Wracamy do aplikacji…",
|
|
29
|
+
"orphaned": "Wylogowano. Możesz zamknąć to okno.",
|
|
30
|
+
"failed": "Wylogowanie nie zostało tu ukończone. Możesz zamknąć to okno.",
|
|
31
|
+
"standalone": {
|
|
32
|
+
"title": "Ta strona służy do wylogowania z innego okna.",
|
|
33
|
+
"action": "Otwórz aplikację"
|
|
34
|
+
},
|
|
35
|
+
"close": "Zamknij"
|
|
36
|
+
}
|
|
5
37
|
}
|
|
6
38
|
}
|
package/src/i18n/auth-ru.json
CHANGED
|
@@ -2,5 +2,37 @@
|
|
|
2
2
|
"dispatcher": {
|
|
3
3
|
"loading": "Загрузка...",
|
|
4
4
|
"error": "Не удалось выполнить аутентификацию: {{error}}"
|
|
5
|
+
},
|
|
6
|
+
"surrogate": {
|
|
7
|
+
"login": {
|
|
8
|
+
"working": "Выполняем вход…",
|
|
9
|
+
"gesture": {
|
|
10
|
+
"title": "Продолжите, чтобы войти.",
|
|
11
|
+
"action": "Войти"
|
|
12
|
+
},
|
|
13
|
+
"handing": "Готово. Возвращаемся в приложение…",
|
|
14
|
+
"orphaned": "Вход выполнен. Можете закрыть это окно и вернуться в приложение.",
|
|
15
|
+
"failed": "Не удалось завершить. Закройте это окно и попробуйте снова.",
|
|
16
|
+
"standalone": {
|
|
17
|
+
"title": "Эта страница используется для входа из другого окна.",
|
|
18
|
+
"action": "Открыть приложение"
|
|
19
|
+
},
|
|
20
|
+
"close": "Закрыть"
|
|
21
|
+
},
|
|
22
|
+
"logout": {
|
|
23
|
+
"working": "Выполняем выход…",
|
|
24
|
+
"gesture": {
|
|
25
|
+
"title": "Продолжите, чтобы выйти.",
|
|
26
|
+
"action": "Выйти"
|
|
27
|
+
},
|
|
28
|
+
"handing": "Выход выполнен. Возвращаемся в приложение…",
|
|
29
|
+
"orphaned": "Выход выполнен. Можете закрыть это окно.",
|
|
30
|
+
"failed": "Выход здесь не завершён. Можете закрыть это окно.",
|
|
31
|
+
"standalone": {
|
|
32
|
+
"title": "Эта страница используется для выхода из другого окна.",
|
|
33
|
+
"action": "Открыть приложение"
|
|
34
|
+
},
|
|
35
|
+
"close": "Закрыть"
|
|
36
|
+
}
|
|
5
37
|
}
|
|
6
38
|
}
|
package/src/i18n/auth-uk.json
CHANGED
|
@@ -2,5 +2,37 @@
|
|
|
2
2
|
"dispatcher": {
|
|
3
3
|
"loading": "Завантаження...",
|
|
4
4
|
"error": "Не вдалося пройти автентифікацію: {{error}}"
|
|
5
|
+
},
|
|
6
|
+
"surrogate": {
|
|
7
|
+
"login": {
|
|
8
|
+
"working": "Виконуємо вхід…",
|
|
9
|
+
"gesture": {
|
|
10
|
+
"title": "Продовжте, щоб увійти.",
|
|
11
|
+
"action": "Увійти"
|
|
12
|
+
},
|
|
13
|
+
"handing": "Готово. Повертаємось до застосунку…",
|
|
14
|
+
"orphaned": "Вхід виконано. Можете закрити це вікно й повернутися до застосунку.",
|
|
15
|
+
"failed": "Не вдалося завершити. Закрийте це вікно та спробуйте ще раз.",
|
|
16
|
+
"standalone": {
|
|
17
|
+
"title": "Ця сторінка використовується для входу з іншого вікна.",
|
|
18
|
+
"action": "Відкрити застосунок"
|
|
19
|
+
},
|
|
20
|
+
"close": "Закрити"
|
|
21
|
+
},
|
|
22
|
+
"logout": {
|
|
23
|
+
"working": "Виконуємо вихід…",
|
|
24
|
+
"gesture": {
|
|
25
|
+
"title": "Продовжте, щоб вийти.",
|
|
26
|
+
"action": "Вийти"
|
|
27
|
+
},
|
|
28
|
+
"handing": "Вихід виконано. Повертаємось до застосунку…",
|
|
29
|
+
"orphaned": "Вихід виконано. Можете закрити це вікно.",
|
|
30
|
+
"failed": "Вихід тут не завершено. Можете закрити це вікно.",
|
|
31
|
+
"standalone": {
|
|
32
|
+
"title": "Ця сторінка використовується для виходу з іншого вікна.",
|
|
33
|
+
"action": "Відкрити застосунок"
|
|
34
|
+
},
|
|
35
|
+
"close": "Закрити"
|
|
36
|
+
}
|
|
5
37
|
}
|
|
6
38
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** The ordinary flow: leave for the provider and come back. */
|
|
2
|
+
export const REDIRECT_LOGIN = 'owlmeans-redirect-login'
|
|
3
|
+
|
|
4
|
+
/** The flow that runs one window up, for a document that cannot redirect where it is. */
|
|
5
|
+
export const SURROGATE_LOGIN = 'owlmeans-surrogate-login'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Registered above the default so it wins the cascade wherever it applies. It applies narrowly —
|
|
9
|
+
* only in a frame, or in the surrogate window itself — so the default still serves every
|
|
10
|
+
* ordinary tab.
|
|
11
|
+
*/
|
|
12
|
+
export const SURROGATE_LOGIN_PRIORITY = 100
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { LOGIN_WATCH_INTERVAL, LoginOutcome } from '@owlmeans/client-auth/login'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wait on a surrogate window until it says something, or goes away.
|
|
5
|
+
*
|
|
6
|
+
* Extracted so login and logout share one implementation rather than two that agree until one of
|
|
7
|
+
* them is changed. Every guard here was a real failure at some point:
|
|
8
|
+
*
|
|
9
|
+
* - the **origin pin** is not decoration — the message carries a bearer token, and a listener that
|
|
10
|
+
* accepted any origin would take one from any page that could reach this window;
|
|
11
|
+
* - the **`settled` latch** stops a second message, or a close racing a message, resolving twice;
|
|
12
|
+
* - the **`closed` poll** is the only exit when the user simply closes the window, because a window
|
|
13
|
+
* the user closes announces nothing at all;
|
|
14
|
+
* - the listener and the interval are removed on every path, including the ones that resolve.
|
|
15
|
+
*/
|
|
16
|
+
export const awaitSurrogate = async (
|
|
17
|
+
surrogate: Window,
|
|
18
|
+
messageType: string,
|
|
19
|
+
onMessage: (data: { type?: string, token?: string, ok?: boolean }) => Promise<LoginOutcome>
|
|
20
|
+
): Promise<LoginOutcome> => await new Promise<LoginOutcome>(resolve => {
|
|
21
|
+
let settled = false
|
|
22
|
+
let watch: ReturnType<typeof setInterval> | undefined
|
|
23
|
+
|
|
24
|
+
const finish = (result: LoginOutcome): void => {
|
|
25
|
+
if (settled) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
settled = true
|
|
29
|
+
window.removeEventListener('message', handler)
|
|
30
|
+
if (watch != null) {
|
|
31
|
+
clearInterval(watch)
|
|
32
|
+
}
|
|
33
|
+
resolve(result)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function handler(event: MessageEvent): void {
|
|
37
|
+
if (event.origin !== window.location.origin) {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
const data = event.data as { type?: string, token?: string, ok?: boolean } | null
|
|
41
|
+
if (data?.type !== messageType) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
void onMessage(data).then(finish).catch(() => finish(LoginOutcome.Failed))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
window.addEventListener('message', handler)
|
|
48
|
+
watch = setInterval(() => {
|
|
49
|
+
if (surrogate.closed) {
|
|
50
|
+
finish(LoginOutcome.Failed)
|
|
51
|
+
}
|
|
52
|
+
}, LOGIN_WATCH_INTERVAL)
|
|
53
|
+
})
|