@owlmeans/client-auth 0.1.0
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/LICENSE +21 -0
- package/README.md +526 -0
- package/build/.gitkeep +0 -0
- package/build/components/dispatcher/component.d.ts +3 -0
- package/build/components/dispatcher/component.d.ts.map +1 -0
- package/build/components/dispatcher/component.js +70 -0
- package/build/components/dispatcher/component.js.map +1 -0
- package/build/components/dispatcher/index.d.ts +3 -0
- package/build/components/dispatcher/index.d.ts.map +1 -0
- package/build/components/dispatcher/index.js +3 -0
- package/build/components/dispatcher/index.js.map +1 -0
- package/build/components/dispatcher/types.d.ts +21 -0
- package/build/components/dispatcher/types.d.ts.map +1 -0
- package/build/components/dispatcher/types.js +2 -0
- package/build/components/dispatcher/types.js.map +1 -0
- package/build/components/index.d.ts +2 -0
- package/build/components/index.d.ts.map +1 -0
- package/build/components/index.js +2 -0
- package/build/components/index.js.map +1 -0
- package/build/consts.d.ts +4 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +4 -0
- package/build/consts.js.map +1 -0
- package/build/helper.d.ts +4 -0
- package/build/helper.d.ts.map +1 -0
- package/build/helper.js +28 -0
- package/build/helper.js.map +1 -0
- package/build/index.d.ts +7 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +6 -0
- package/build/index.js.map +1 -0
- package/build/manager/components/authentication/component.d.ts +3 -0
- package/build/manager/components/authentication/component.d.ts.map +1 -0
- package/build/manager/components/authentication/component.js +28 -0
- package/build/manager/components/authentication/component.js.map +1 -0
- package/build/manager/components/authentication/consts.d.ts +2 -0
- package/build/manager/components/authentication/consts.d.ts.map +1 -0
- package/build/manager/components/authentication/consts.js +2 -0
- package/build/manager/components/authentication/consts.js.map +1 -0
- package/build/manager/components/authentication/control.d.ts +6 -0
- package/build/manager/components/authentication/control.d.ts.map +1 -0
- package/build/manager/components/authentication/control.js +146 -0
- package/build/manager/components/authentication/control.js.map +1 -0
- package/build/manager/components/authentication/index.d.ts +4 -0
- package/build/manager/components/authentication/index.d.ts.map +1 -0
- package/build/manager/components/authentication/index.js +4 -0
- package/build/manager/components/authentication/index.js.map +1 -0
- package/build/manager/components/authentication/types.d.ts +57 -0
- package/build/manager/components/authentication/types.d.ts.map +1 -0
- package/build/manager/components/authentication/types.js +2 -0
- package/build/manager/components/authentication/types.js.map +1 -0
- package/build/manager/components/index.d.ts +4 -0
- package/build/manager/components/index.d.ts.map +1 -0
- package/build/manager/components/index.js +3 -0
- package/build/manager/components/index.js.map +1 -0
- package/build/manager/components/tunnel-consumer.d.ts +4 -0
- package/build/manager/components/tunnel-consumer.d.ts.map +1 -0
- package/build/manager/components/tunnel-consumer.js +11 -0
- package/build/manager/components/tunnel-consumer.js.map +1 -0
- package/build/manager/components/types.d.ts +10 -0
- package/build/manager/components/types.d.ts.map +1 -0
- package/build/manager/components/types.js +2 -0
- package/build/manager/components/types.js.map +1 -0
- package/build/manager/errors.d.ts +6 -0
- package/build/manager/errors.d.ts.map +1 -0
- package/build/manager/errors.js +11 -0
- package/build/manager/errors.js.map +1 -0
- package/build/manager/index.d.ts +4 -0
- package/build/manager/index.d.ts.map +1 -0
- package/build/manager/index.js +4 -0
- package/build/manager/index.js.map +1 -0
- package/build/manager/modules.d.ts +2 -0
- package/build/manager/modules.d.ts.map +1 -0
- package/build/manager/modules.js +16 -0
- package/build/manager/modules.js.map +1 -0
- package/build/manager/plugins/basic-ed25519.d.ts +3 -0
- package/build/manager/plugins/basic-ed25519.d.ts.map +1 -0
- package/build/manager/plugins/basic-ed25519.js +33 -0
- package/build/manager/plugins/basic-ed25519.js.map +1 -0
- package/build/manager/plugins/exports.d.ts +6 -0
- package/build/manager/plugins/exports.d.ts.map +1 -0
- package/build/manager/plugins/exports.js +5 -0
- package/build/manager/plugins/exports.js.map +1 -0
- package/build/manager/plugins/index.d.ts +6 -0
- package/build/manager/plugins/index.d.ts.map +1 -0
- package/build/manager/plugins/index.js +9 -0
- package/build/manager/plugins/index.js.map +1 -0
- package/build/manager/plugins/re-captcha.d.ts +3 -0
- package/build/manager/plugins/re-captcha.d.ts.map +1 -0
- package/build/manager/plugins/re-captcha.js +26 -0
- package/build/manager/plugins/re-captcha.js.map +1 -0
- package/build/manager/plugins/tunnel/consts.d.ts +4 -0
- package/build/manager/plugins/tunnel/consts.d.ts.map +1 -0
- package/build/manager/plugins/tunnel/consts.js +9 -0
- package/build/manager/plugins/tunnel/consts.js.map +1 -0
- package/build/manager/plugins/tunnel/index.d.ts +4 -0
- package/build/manager/plugins/tunnel/index.d.ts.map +1 -0
- package/build/manager/plugins/tunnel/index.js +3 -0
- package/build/manager/plugins/tunnel/index.js.map +1 -0
- package/build/manager/plugins/tunnel/types.d.ts +13 -0
- package/build/manager/plugins/tunnel/types.d.ts.map +1 -0
- package/build/manager/plugins/tunnel/types.js +2 -0
- package/build/manager/plugins/tunnel/types.js.map +1 -0
- package/build/manager/plugins/tunnel/wallet.d.ts +4 -0
- package/build/manager/plugins/tunnel/wallet.d.ts.map +1 -0
- package/build/manager/plugins/tunnel/wallet.js +32 -0
- package/build/manager/plugins/tunnel/wallet.js.map +1 -0
- package/build/manager/plugins/tunnel-consumer.d.ts +3 -0
- package/build/manager/plugins/tunnel-consumer.d.ts.map +1 -0
- package/build/manager/plugins/tunnel-consumer.js +81 -0
- package/build/manager/plugins/tunnel-consumer.js.map +1 -0
- package/build/manager/plugins/types.d.ts +11 -0
- package/build/manager/plugins/types.d.ts.map +1 -0
- package/build/manager/plugins/types.js +2 -0
- package/build/manager/plugins/types.js.map +1 -0
- package/build/modules.d.ts +4 -0
- package/build/modules.d.ts.map +1 -0
- package/build/modules.js +9 -0
- package/build/modules.js.map +1 -0
- package/build/service.d.ts +6 -0
- package/build/service.d.ts.map +1 -0
- package/build/service.js +71 -0
- package/build/service.js.map +1 -0
- package/build/types.d.ts +13 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/package.json +79 -0
- package/src/components/dispatcher/component.tsx +84 -0
- package/src/components/dispatcher/index.ts +3 -0
- package/src/components/dispatcher/types.ts +24 -0
- package/src/components/index.ts +2 -0
- package/src/consts.ts +6 -0
- package/src/helper.ts +39 -0
- package/src/index.ts +7 -0
- package/src/manager/README.md +463 -0
- package/src/manager/components/authentication/component.tsx +34 -0
- package/src/manager/components/authentication/consts.ts +2 -0
- package/src/manager/components/authentication/control.ts +193 -0
- package/src/manager/components/authentication/index.ts +4 -0
- package/src/manager/components/authentication/types.ts +74 -0
- package/src/manager/components/index.ts +4 -0
- package/src/manager/components/tunnel-consumer.tsx +15 -0
- package/src/manager/components/types.ts +11 -0
- package/src/manager/errors.ts +14 -0
- package/src/manager/index.ts +4 -0
- package/src/manager/modules.ts +18 -0
- package/src/manager/plugins/basic-ed25519.tsx +42 -0
- package/src/manager/plugins/exports.ts +5 -0
- package/src/manager/plugins/index.ts +13 -0
- package/src/manager/plugins/re-captcha.tsx +31 -0
- package/src/manager/plugins/tunnel/consts.ts +12 -0
- package/src/manager/plugins/tunnel/index.ts +5 -0
- package/src/manager/plugins/tunnel/types.ts +15 -0
- package/src/manager/plugins/tunnel/wallet.ts +43 -0
- package/src/manager/plugins/tunnel-consumer.tsx +100 -0
- package/src/manager/plugins/types.ts +14 -0
- package/src/modules.ts +13 -0
- package/src/service.ts +106 -0
- package/src/types.ts +15 -0
- package/tsconfig.json +15 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { AuthenticationControl } from './types.js'
|
|
2
|
+
import {
|
|
3
|
+
ALL_SCOPES, AUTHEN_AUTHEN, AUTHEN_INIT, AuthRole, AuthenticationStage, AuthenticationType,
|
|
4
|
+
} from '@owlmeans/auth'
|
|
5
|
+
import type { AllowanceResponse, AllowanceRequest, AuthToken, AuthCredentials } from '@owlmeans/auth'
|
|
6
|
+
import type { ClientContext } from '@owlmeans/client'
|
|
7
|
+
import type { ClientConfig } from '@owlmeans/client-context'
|
|
8
|
+
import type { ClientModule } from '@owlmeans/client-module'
|
|
9
|
+
import { AuthenCredError } from '../../errors.js'
|
|
10
|
+
import { plugins } from '../../plugins/index.js'
|
|
11
|
+
import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope'
|
|
12
|
+
import type { EnvelopeModel } from '@owlmeans/basic-envelope'
|
|
13
|
+
import { ModuleOutcome } from '@owlmeans/module'
|
|
14
|
+
import { DEFAULT_ALIAS as FLOW_SERVICE, FLOW_STATE } from '@owlmeans/client-flow'
|
|
15
|
+
import type { FlowService, StateResource } from '@owlmeans/client-flow'
|
|
16
|
+
import { CONTROL_STATE_ID } from './consts.js'
|
|
17
|
+
import { ResilientError } from '@owlmeans/error'
|
|
18
|
+
|
|
19
|
+
export const makeControl = (
|
|
20
|
+
context: ClientContext<ClientConfig>,
|
|
21
|
+
callback?: (token: AuthToken) => Promise<boolean>
|
|
22
|
+
): AuthenticationControl => {
|
|
23
|
+
// @TODO: This control should deal with scopes someway
|
|
24
|
+
const control: AuthenticationControl = {
|
|
25
|
+
|
|
26
|
+
stage: AuthenticationStage.Init,
|
|
27
|
+
|
|
28
|
+
type: AuthenticationType.BasicEd25519,
|
|
29
|
+
|
|
30
|
+
callback,
|
|
31
|
+
|
|
32
|
+
setError: async error => {
|
|
33
|
+
control.error = ResilientError.ensure(error as Error)
|
|
34
|
+
|
|
35
|
+
control.updateStage(AuthenticationStage.Error)
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
requestAllowence: async request => {
|
|
39
|
+
control.updateStage(AuthenticationStage.Allowence)
|
|
40
|
+
|
|
41
|
+
control.request = (request ?? { type: control.type }) as AllowanceRequest
|
|
42
|
+
control.type = control.request.type as string
|
|
43
|
+
|
|
44
|
+
control.beforeAuthenticate = plugins[control.type].beforeAuthenticate
|
|
45
|
+
control.afterAuthenticate = plugins[control.type].afterAuthenticate
|
|
46
|
+
|
|
47
|
+
const module = context.module<ClientModule<AllowanceResponse>>(AUTHEN_INIT)
|
|
48
|
+
const [allowance] = await module.call({ body: control.request })
|
|
49
|
+
|
|
50
|
+
control.allowance = allowance
|
|
51
|
+
|
|
52
|
+
control.updateStage(AuthenticationStage.Authenticate)
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
authenticate: async credentials => {
|
|
56
|
+
control.updateStage(AuthenticationStage.Authentication)
|
|
57
|
+
try {
|
|
58
|
+
credentials.type = control.type
|
|
59
|
+
if (control.allowance?.challenge == null) {
|
|
60
|
+
throw new AuthenCredError('allowance')
|
|
61
|
+
}
|
|
62
|
+
const envelope: EnvelopeModel = makeEnvelopeModel(control.allowance?.challenge, EnvelopeKind.Wrap)
|
|
63
|
+
|
|
64
|
+
credentials.challenge = envelope.message()
|
|
65
|
+
credentials.scopes = credentials.scopes ?? [ALL_SCOPES]
|
|
66
|
+
credentials.role = credentials.role ?? AuthRole.User
|
|
67
|
+
|
|
68
|
+
if (credentials.credential == null || credentials.credential === '') {
|
|
69
|
+
throw new AuthenCredError('credential')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (credentials.userId == null || credentials.userId === '') {
|
|
73
|
+
throw new AuthenCredError('userId')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (credentials.challenge == null || credentials.challenge === '') {
|
|
77
|
+
throw new AuthenCredError('challenge')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (plugins[control.type].authenticate != null) {
|
|
81
|
+
// We sign unwrapped challenge (or do something similar)
|
|
82
|
+
const clientToken = await plugins[control.type].authenticate!(credentials, context)
|
|
83
|
+
|
|
84
|
+
if (clientToken.token !== '' && control.beforeAuthenticate != null) {
|
|
85
|
+
control.beforeAuthenticate(clientToken, context)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// We return back unwrapped challenge
|
|
90
|
+
credentials.challenge = control.allowance?.challenge
|
|
91
|
+
|
|
92
|
+
const [token, status] = await context.module<ClientModule<AuthToken>>(AUTHEN_AUTHEN)
|
|
93
|
+
.call({ body: credentials })
|
|
94
|
+
|
|
95
|
+
if (status === ModuleOutcome.Ok && token.token != null
|
|
96
|
+
&& token.token !== '' && control.afterAuthenticate != null) {
|
|
97
|
+
const resultingCred = makeEnvelopeModel<AuthCredentials>(token.token, EnvelopeKind.Token).message()
|
|
98
|
+
await control.afterAuthenticate(resultingCred, context)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (control.callback != null && await control.callback(token)) {
|
|
102
|
+
return { token: '' }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return token
|
|
106
|
+
} catch (error) {
|
|
107
|
+
// @TODO we need to move this processing to all respective UI implementations
|
|
108
|
+
// control.setStage?.(control.stage = AuthenticationStage.Authenticate)
|
|
109
|
+
throw error
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
updateStage: stage => {
|
|
114
|
+
control.stage = stage
|
|
115
|
+
control.setStage?.(stage)
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
persist: async () => {
|
|
119
|
+
if (context.hasResource(FLOW_STATE)) {
|
|
120
|
+
const resource = context.resource<StateResource>(FLOW_STATE)
|
|
121
|
+
await resource.save({
|
|
122
|
+
id: CONTROL_STATE_ID, ...{
|
|
123
|
+
type: control.type,
|
|
124
|
+
stage: control.stage,
|
|
125
|
+
allowance: control.allowance,
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return true
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return false
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
restore: async () => {
|
|
136
|
+
if (context.hasResource(FLOW_STATE)) {
|
|
137
|
+
const resource = context.resource<StateResource>(FLOW_STATE)
|
|
138
|
+
const state = await resource.load(CONTROL_STATE_ID)
|
|
139
|
+
if (state != null) {
|
|
140
|
+
const _state = state as unknown as Partial<AuthenticationControl>
|
|
141
|
+
if (_state.type != null) {
|
|
142
|
+
control.type = _state.type
|
|
143
|
+
}
|
|
144
|
+
control.beforeAuthenticate = plugins[control.type].beforeAuthenticate
|
|
145
|
+
control.afterAuthenticate = plugins[control.type].afterAuthenticate
|
|
146
|
+
|
|
147
|
+
if (_state.stage != null) {
|
|
148
|
+
control.stage = _state.stage
|
|
149
|
+
}
|
|
150
|
+
if (_state.allowance != null) {
|
|
151
|
+
control.allowance = _state.allowance
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return true
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return false
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
hasPersistentState: async () => {
|
|
161
|
+
if (context.hasResource(FLOW_STATE)) {
|
|
162
|
+
const resource = context.resource<StateResource>(FLOW_STATE)
|
|
163
|
+
if (null != await resource.load(CONTROL_STATE_ID)) {
|
|
164
|
+
return true
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return false
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
cleanUpState: async () => {
|
|
172
|
+
if (context.hasResource(FLOW_STATE)) {
|
|
173
|
+
const resource = context.resource<StateResource>(FLOW_STATE)
|
|
174
|
+
await resource.delete(CONTROL_STATE_ID)
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
flow: async () => {
|
|
179
|
+
const alias = FLOW_SERVICE
|
|
180
|
+
if (context.hasService(alias)) {
|
|
181
|
+
const flow = context.service<FlowService>(alias)
|
|
182
|
+
await flow.ready()
|
|
183
|
+
if (await flow.supplied) {
|
|
184
|
+
return flow
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return control
|
|
193
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AllowanceRequest, AllowanceResponse, AuthCredentials, AuthenticationStage, AuthenticationType,
|
|
3
|
+
AuthToken
|
|
4
|
+
} from '@owlmeans/auth'
|
|
5
|
+
import type { ModuleContextParams } from '@owlmeans/client'
|
|
6
|
+
import type { FC } from 'react'
|
|
7
|
+
import type { ClientContext } from '@owlmeans/client'
|
|
8
|
+
import type { FlowService } from '@owlmeans/client-flow'
|
|
9
|
+
import type { ResilientError } from '@owlmeans/error'
|
|
10
|
+
|
|
11
|
+
export type ClientAuthType = AuthenticationType | string
|
|
12
|
+
|
|
13
|
+
export interface AuthenticationProps extends ModuleContextParams {
|
|
14
|
+
type?: ClientAuthType
|
|
15
|
+
callback?: AuthenticationCallback
|
|
16
|
+
source?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface TAuthenticationHOC {
|
|
20
|
+
(Renderer?: AuthenticationRenderer, type?: string): FC<AuthenticationProps>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AuthenticationRendererProps {
|
|
24
|
+
stage: AuthenticationStage
|
|
25
|
+
type: ClientAuthType
|
|
26
|
+
control: AuthenticationControl
|
|
27
|
+
params: Record<string, unknown>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface AuthenticationRenderer extends FC<AuthenticationRendererProps> {
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface AuthenticationControlState {
|
|
34
|
+
stage: AuthenticationStage
|
|
35
|
+
type: ClientAuthType
|
|
36
|
+
request?: AllowanceRequest
|
|
37
|
+
allowance?: AllowanceResponse
|
|
38
|
+
error?: ResilientError
|
|
39
|
+
source?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface AuthenticationControl extends AuthenticationControlState {
|
|
43
|
+
callback?: AuthenticationCallback
|
|
44
|
+
setStage?: (stage: AuthenticationStage) => void
|
|
45
|
+
updateStage: (stage: AuthenticationStage) => void
|
|
46
|
+
requestAllowence: (request?: Partial<AllowanceRequest>) => Promise<void>
|
|
47
|
+
beforeAuthenticate?: (clientToken: AuthToken, context?: ClientContext) => Promise<void>
|
|
48
|
+
afterAuthenticate?: (credential: AuthCredentials, context?: ClientContext) => Promise<void>
|
|
49
|
+
authenticate: ClientAuthenticationMethod
|
|
50
|
+
|
|
51
|
+
flow: () => Promise<FlowService | null>
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* These methods are used to store control state in cases when the screen is left
|
|
55
|
+
* during authentication process. E.g. for OIDC authentication on web
|
|
56
|
+
*/
|
|
57
|
+
persist: () => Promise<boolean>
|
|
58
|
+
restore: () => Promise<boolean>
|
|
59
|
+
hasPersistentState: () => Promise<boolean>
|
|
60
|
+
cleanUpState: () => Promise<void>
|
|
61
|
+
|
|
62
|
+
setError: (error: unknown) => Promise<void>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ClientAuthenticationMethod {
|
|
66
|
+
(
|
|
67
|
+
credential: Partial<AuthCredentials> & Pick<AuthCredentials, "userId" | "credential">,
|
|
68
|
+
context?: ClientContext
|
|
69
|
+
): Promise<AuthToken>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface AuthenticationCallback {
|
|
73
|
+
(token: AuthToken, ...args: any[]): Promise<boolean>
|
|
74
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AuthenticationType } from '@owlmeans/auth'
|
|
2
|
+
import { AuthenticationHOC } from './authentication/component.js'
|
|
3
|
+
import type { FC } from 'react'
|
|
4
|
+
import { useMemo } from 'react'
|
|
5
|
+
import { useModule } from '@owlmeans/client'
|
|
6
|
+
import type { TunnelAuthenticationProps } from './types.js'
|
|
7
|
+
|
|
8
|
+
export const TunnelConsumer: FC<Partial<TunnelAuthenticationProps>> = ({ ...props }) => {
|
|
9
|
+
const mod = useModule()
|
|
10
|
+
const Implementation = useMemo(
|
|
11
|
+
() => AuthenticationHOC(undefined, AuthenticationType.WalletConsumer), []
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
return <Implementation {...mod} {...props} type={AuthenticationType.WalletConsumer}/>
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AuthToken } from '@owlmeans/auth'
|
|
2
|
+
import type { WalletFacade } from '@owlmeans/did'
|
|
3
|
+
import type { AuthenticationProps } from './authentication/types.js'
|
|
4
|
+
|
|
5
|
+
export interface TunnelAuthenticationProps extends AuthenticationProps {
|
|
6
|
+
callback: TunnelAuthCallback
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface TunnelAuthCallback {
|
|
10
|
+
(token: AuthToken, wallet: WalletFacade): Promise<boolean>
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
import { ResilientError } from '@owlmeans/error'
|
|
3
|
+
import { AuthManagerError } from '@owlmeans/auth'
|
|
4
|
+
|
|
5
|
+
export class AuthenCredError extends AuthManagerError {
|
|
6
|
+
public static override typeName: string = `${AuthManagerError.typeName}Cred`
|
|
7
|
+
|
|
8
|
+
constructor(message: string = 'error') {
|
|
9
|
+
super(`cred:${message}`)
|
|
10
|
+
this.type = AuthenCredError.typeName
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
ResilientError.registerErrorClass(AuthenCredError)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
import { AUTHEN, AUTHEN_AUTHEN, AUTHEN_INIT, AUTHEN_RELY, CAUTHEN, CAUTHEN_AUTHEN, CAUTHEN_AUTHEN_DEFAULT, CAUTHEN_AUTHEN_TYPED, DISPATCHER } from '@owlmeans/auth'
|
|
3
|
+
import { modules as list } from '@owlmeans/auth-common'
|
|
4
|
+
import { handler } from '@owlmeans/client'
|
|
5
|
+
import { elevate, stab } from '@owlmeans/client-module'
|
|
6
|
+
import { AuthenticationHOC } from './components/authentication/component.js'
|
|
7
|
+
|
|
8
|
+
elevate(list, AUTHEN)
|
|
9
|
+
elevate(list, AUTHEN_INIT, true)
|
|
10
|
+
elevate(list, AUTHEN_AUTHEN, true)
|
|
11
|
+
elevate(list, AUTHEN_RELY)
|
|
12
|
+
elevate(list, CAUTHEN)
|
|
13
|
+
elevate(list, CAUTHEN_AUTHEN)
|
|
14
|
+
elevate(list, CAUTHEN_AUTHEN_DEFAULT, handler(AuthenticationHOC()))
|
|
15
|
+
elevate(list, CAUTHEN_AUTHEN_TYPED, handler(AuthenticationHOC()))
|
|
16
|
+
elevate(list, DISPATCHER, stab, { force: true })
|
|
17
|
+
|
|
18
|
+
export const modules = list
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AuthenticationStage, AuthenticationType } from '@owlmeans/auth'
|
|
2
|
+
import type { AuthenticationPlugin } from './types.js'
|
|
3
|
+
import { useEffect } from 'react'
|
|
4
|
+
import { makeKeyPairModel } from '@owlmeans/basic-keys'
|
|
5
|
+
import { AuthenCredError } from '../errors.js'
|
|
6
|
+
|
|
7
|
+
export const ed25519BasicUIPlugin: AuthenticationPlugin = {
|
|
8
|
+
type: AuthenticationType.BasicEd25519,
|
|
9
|
+
|
|
10
|
+
Implementation: Renderer => ({ type, stage, control, params }) => {
|
|
11
|
+
|
|
12
|
+
type = type ?? AuthenticationType.BasicEd25519
|
|
13
|
+
Renderer = Renderer ?? ed25519BasicUIPlugin.Renderer
|
|
14
|
+
|
|
15
|
+
// BasicEd25519 authentication request allowance unconditionally
|
|
16
|
+
// (no input or additional challenges required)
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (control.stage === AuthenticationStage.Init) {
|
|
19
|
+
void control.requestAllowence()
|
|
20
|
+
}
|
|
21
|
+
}, [type])
|
|
22
|
+
|
|
23
|
+
if (Renderer == null) {
|
|
24
|
+
throw new SyntaxError('Renderer is not defined for BasicEd25519 plugin')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return <Renderer type={type} stage={stage} control={control} params={params} />
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
authenticate: async credentials => {
|
|
31
|
+
const key = makeKeyPairModel(credentials.credential)
|
|
32
|
+
|
|
33
|
+
if (credentials.userId !== key.exportAddress()) {
|
|
34
|
+
throw new AuthenCredError('credentials-missmatch')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
credentials.credential = await key.sign(credentials.challenge)
|
|
38
|
+
|
|
39
|
+
// We don't use it - just type compatibility
|
|
40
|
+
return { token: '' }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ClientAuthType } from '../components/index.js'
|
|
2
|
+
|
|
3
|
+
import { AuthenticationType } from '@owlmeans/auth'
|
|
4
|
+
import { ed25519BasicUIPlugin } from './basic-ed25519.js'
|
|
5
|
+
import { AuthenticationPlugin } from './types.js'
|
|
6
|
+
import { reCaptchaPlugin } from './re-captcha.js'
|
|
7
|
+
import { tunnelConsumerUIPlugin } from './tunnel-consumer.js'
|
|
8
|
+
|
|
9
|
+
export const plugins: { [type: ClientAuthType]: AuthenticationPlugin } = {}
|
|
10
|
+
|
|
11
|
+
plugins[AuthenticationType.BasicEd25519] = ed25519BasicUIPlugin
|
|
12
|
+
plugins[AuthenticationType.ReCaptcha] = reCaptchaPlugin
|
|
13
|
+
plugins[AuthenticationType.WalletConsumer] = tunnelConsumerUIPlugin
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AuthenticationStage, AuthenticationType } from '@owlmeans/auth'
|
|
2
|
+
import type { AuthenticationPlugin } from './types.js'
|
|
3
|
+
import { useEffect } from 'react'
|
|
4
|
+
|
|
5
|
+
export const reCaptchaPlugin: AuthenticationPlugin = {
|
|
6
|
+
type: AuthenticationType.ReCaptcha,
|
|
7
|
+
|
|
8
|
+
Implementation: Renderer => ({ type, stage, control, params }) => {
|
|
9
|
+
Renderer = Renderer ?? reCaptchaPlugin.Renderer
|
|
10
|
+
|
|
11
|
+
// ReCaptcha authentication requests allowance unconditionally
|
|
12
|
+
// (no input or additional challenges required)
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (control.stage === AuthenticationStage.Init) {
|
|
15
|
+
void control.requestAllowence({ type, source: control.source })
|
|
16
|
+
}
|
|
17
|
+
}, [type])
|
|
18
|
+
|
|
19
|
+
if (Renderer == null) {
|
|
20
|
+
throw new SyntaxError('Renderer is not defined for ReCaptcha plugin')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return <Renderer type={type} stage={stage} control={control} params={params} />
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Actually we don't need to do anything here cause ReCaptcha server
|
|
28
|
+
* did it for us.
|
|
29
|
+
*/
|
|
30
|
+
authenticate: async () => ({ token: '' })
|
|
31
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
import type { JSONSchemaType } from 'ajv'
|
|
3
|
+
import type { PinForm } from './types.js'
|
|
4
|
+
|
|
5
|
+
export const PinSchema: JSONSchemaType<PinForm> = {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
pin: { type: 'string', minLength: 2, maxLength: 12 }
|
|
9
|
+
},
|
|
10
|
+
required: ['pin'],
|
|
11
|
+
additionalProperties: false,
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AuthenticationRendererProps } from '../../components/authentication/types.js'
|
|
2
|
+
import type { FC } from 'react'
|
|
3
|
+
import type { Connection } from '@owlmeans/socket'
|
|
4
|
+
|
|
5
|
+
export interface TunnelAuthenticationRenderer extends FC<TunnelAuthenticationRendererProps> {
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface TunnelAuthenticationRendererProps extends AuthenticationRendererProps {
|
|
9
|
+
conn: Connection | null
|
|
10
|
+
submit: (data: PinForm) => Promise<void>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface PinForm {
|
|
14
|
+
pin: string
|
|
15
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { WalletFacade } from '@owlmeans/did'
|
|
2
|
+
import type { Connection } from '@owlmeans/socket'
|
|
3
|
+
import { base64, base64urlnopad } from '@scure/base'
|
|
4
|
+
|
|
5
|
+
export const createWalletFacade = (conn: Connection): WalletFacade => {
|
|
6
|
+
const facade: WalletFacade = {
|
|
7
|
+
sign: async (entityId, payload, opts) => {
|
|
8
|
+
const result: string = await conn.call('sign', entityId, base64.encode(payload), opts)
|
|
9
|
+
|
|
10
|
+
return base64urlnopad.decode(result)
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
createKey: async (entityId, opts) => {
|
|
14
|
+
return await conn.call('createKey', entityId, opts)
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
getEntityKey: async (entityId, opts) => {
|
|
18
|
+
return await conn.call('getEntityKey', entityId, opts)
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
getMasterDid: async opts => {
|
|
22
|
+
return await conn.call('getMasterDid', opts)
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
selectKey: async opts => {
|
|
26
|
+
return await conn.call('selectKey', opts)
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
getPublicDetails: async (did, opts) => {
|
|
30
|
+
return await conn.call('getPublicDetails', did, opts)
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
requestPermissions: async (methods, opts) => {
|
|
34
|
+
return await conn.call('requestPermissions', methods, opts)
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
close: async () => {
|
|
38
|
+
await conn.close()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return facade
|
|
43
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { useWs } from '@owlmeans/client-socket'
|
|
2
|
+
import { AUTH_SCOPE, AUTHEN_RELY, AuthenticationStage, AuthenticationType, AuthRole } from '@owlmeans/auth'
|
|
3
|
+
import type { AllowanceRequest, AllowanceResponse, Auth, AuthCredentials } from '@owlmeans/auth'
|
|
4
|
+
import type { AuthenticationPlugin } from './types.js'
|
|
5
|
+
import type { PinForm, TunnelAuthenticationRenderer } from './tunnel/types.js'
|
|
6
|
+
import { useCallback, useEffect } from 'react'
|
|
7
|
+
import type { Connection } from '@owlmeans/socket'
|
|
8
|
+
import { isEventMessage, SocketTimeout } from '@owlmeans/socket'
|
|
9
|
+
import type { AuthenticationControl } from '../components/authentication/types.js'
|
|
10
|
+
import { RELY_ACTION_TIMEOUT, RELY_PIN_PERFIX } from '@owlmeans/auth-common'
|
|
11
|
+
import { createWalletFacade } from './tunnel/wallet.js'
|
|
12
|
+
|
|
13
|
+
export const tunnelConsumerUIPlugin: AuthenticationPlugin = {
|
|
14
|
+
type: AuthenticationType.WalletConsumer,
|
|
15
|
+
|
|
16
|
+
Implementation: renderer => ({ type, stage, control, params }) => {
|
|
17
|
+
type = type ?? AuthenticationType.WalletConsumer
|
|
18
|
+
const Renderer: TunnelAuthenticationRenderer | undefined = renderer
|
|
19
|
+
?? tunnelConsumerUIPlugin.Renderer
|
|
20
|
+
|
|
21
|
+
const connection = useWs(AUTHEN_RELY)
|
|
22
|
+
|
|
23
|
+
if (Renderer == null) {
|
|
24
|
+
throw new SyntaxError('Renderer is not defined for WalletConsumer plugin')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (connection != null) {
|
|
29
|
+
control.updateStage(AuthenticationStage.Allowence)
|
|
30
|
+
connection.auth<AllowanceRequest, AllowanceResponse>(
|
|
31
|
+
AuthenticationStage.Init, { type: AuthenticationType.RelyHandshake }
|
|
32
|
+
).then(async (allowance: AllowanceResponse) => {
|
|
33
|
+
control.allowance = allowance
|
|
34
|
+
control.updateStage(AuthenticationStage.Authenticate)
|
|
35
|
+
|
|
36
|
+
connection.defaultCallTimeout = RELY_ACTION_TIMEOUT * 1000
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
connection.authenticate = async (stage: AuthenticationStage, payload: any) => {
|
|
40
|
+
if (stage === AuthenticationStage.Authenticated) {
|
|
41
|
+
try {
|
|
42
|
+
if (control.callback == null) {
|
|
43
|
+
throw new SyntaxError('Tunnel consumer requires a callback to provide connection object')
|
|
44
|
+
}
|
|
45
|
+
await control.callback(payload, createWalletFacade(connection))
|
|
46
|
+
control.updateStage(AuthenticationStage.Authentication)
|
|
47
|
+
} catch (e) {
|
|
48
|
+
await connection.close()
|
|
49
|
+
await control.setError(e)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return [null, null as any]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return connection.listen(async message => {
|
|
56
|
+
if (isEventMessage(message) && message.event === 'close') {
|
|
57
|
+
await control.setError(new SocketTimeout('rely'))
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}, [connection])
|
|
62
|
+
|
|
63
|
+
const submit = useCallback(
|
|
64
|
+
connection != null ? makeSubmit(connection, control) : async () => void 0
|
|
65
|
+
, [connection != null]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return <Renderer type={type} stage={stage} control={control} conn={connection} params={params} submit={submit} />
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
authenticate: async _credentials => {
|
|
72
|
+
// We don't use it - just type compatibility
|
|
73
|
+
return { token: '' }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const makeSubmit = (conn: Connection, control: AuthenticationControl) => async (data: PinForm) => {
|
|
78
|
+
await control.updateStage(AuthenticationStage.Authentication)
|
|
79
|
+
try {
|
|
80
|
+
const auth = await conn.auth<AuthCredentials, Auth>(AuthenticationStage.Authenticate, {
|
|
81
|
+
challenge: control.allowance?.challenge ?? '',
|
|
82
|
+
credential: RELY_PIN_PERFIX + data.pin,
|
|
83
|
+
type: control.type,
|
|
84
|
+
role: AuthRole.Guest,
|
|
85
|
+
userId: '',
|
|
86
|
+
scopes: [AUTH_SCOPE]
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
if (conn.stage === AuthenticationStage.Authenticated) {
|
|
90
|
+
if (control.callback == null) {
|
|
91
|
+
throw new SyntaxError('Tunnel consumer requires a callback to provide connection object')
|
|
92
|
+
}
|
|
93
|
+
await control.callback(auth, createWalletFacade(conn))
|
|
94
|
+
}
|
|
95
|
+
// (auth)
|
|
96
|
+
} catch (e) {
|
|
97
|
+
await conn.close()
|
|
98
|
+
await control.setError(e)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FC } from 'react'
|
|
2
|
+
import type { AuthenticationControl, AuthenticationRenderer, AuthenticationRendererProps, } from '../components/authentication/types.js'
|
|
3
|
+
|
|
4
|
+
export interface AuthenticationPlugin extends Pick<
|
|
5
|
+
AuthenticationControl, "beforeAuthenticate" | "afterAuthenticate"
|
|
6
|
+
>, Pick<Partial<AuthenticationControl>, "authenticate"> {
|
|
7
|
+
type: string
|
|
8
|
+
Implementation: PluginImplemnetation
|
|
9
|
+
Renderer?: AuthenticationRenderer
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PluginImplemnetation {
|
|
13
|
+
(Renderer?: AuthenticationRenderer): FC<AuthenticationRendererProps>
|
|
14
|
+
}
|
package/src/modules.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
import { elevate, stab } from '@owlmeans/client-module'
|
|
3
|
+
import { modules as list } from '@owlmeans/auth-common'
|
|
4
|
+
import { CAUTHEN_FLOW_ENTER, DISPATCHER_AUTHEN } from '@owlmeans/auth'
|
|
5
|
+
import type { ClientModule } from '@owlmeans/client-module'
|
|
6
|
+
|
|
7
|
+
elevate(list, DISPATCHER_AUTHEN)
|
|
8
|
+
|
|
9
|
+
export const modules: ClientModule[] = list as ClientModule[]
|
|
10
|
+
|
|
11
|
+
export const setupExternalAuthentication = (service: string) => {
|
|
12
|
+
elevate(list, CAUTHEN_FLOW_ENTER, stab, { routeOptions: { overrides: { service } } })
|
|
13
|
+
}
|