@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.
Files changed (162) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +526 -0
  3. package/build/.gitkeep +0 -0
  4. package/build/components/dispatcher/component.d.ts +3 -0
  5. package/build/components/dispatcher/component.d.ts.map +1 -0
  6. package/build/components/dispatcher/component.js +70 -0
  7. package/build/components/dispatcher/component.js.map +1 -0
  8. package/build/components/dispatcher/index.d.ts +3 -0
  9. package/build/components/dispatcher/index.d.ts.map +1 -0
  10. package/build/components/dispatcher/index.js +3 -0
  11. package/build/components/dispatcher/index.js.map +1 -0
  12. package/build/components/dispatcher/types.d.ts +21 -0
  13. package/build/components/dispatcher/types.d.ts.map +1 -0
  14. package/build/components/dispatcher/types.js +2 -0
  15. package/build/components/dispatcher/types.js.map +1 -0
  16. package/build/components/index.d.ts +2 -0
  17. package/build/components/index.d.ts.map +1 -0
  18. package/build/components/index.js +2 -0
  19. package/build/components/index.js.map +1 -0
  20. package/build/consts.d.ts +4 -0
  21. package/build/consts.d.ts.map +1 -0
  22. package/build/consts.js +4 -0
  23. package/build/consts.js.map +1 -0
  24. package/build/helper.d.ts +4 -0
  25. package/build/helper.d.ts.map +1 -0
  26. package/build/helper.js +28 -0
  27. package/build/helper.js.map +1 -0
  28. package/build/index.d.ts +7 -0
  29. package/build/index.d.ts.map +1 -0
  30. package/build/index.js +6 -0
  31. package/build/index.js.map +1 -0
  32. package/build/manager/components/authentication/component.d.ts +3 -0
  33. package/build/manager/components/authentication/component.d.ts.map +1 -0
  34. package/build/manager/components/authentication/component.js +28 -0
  35. package/build/manager/components/authentication/component.js.map +1 -0
  36. package/build/manager/components/authentication/consts.d.ts +2 -0
  37. package/build/manager/components/authentication/consts.d.ts.map +1 -0
  38. package/build/manager/components/authentication/consts.js +2 -0
  39. package/build/manager/components/authentication/consts.js.map +1 -0
  40. package/build/manager/components/authentication/control.d.ts +6 -0
  41. package/build/manager/components/authentication/control.d.ts.map +1 -0
  42. package/build/manager/components/authentication/control.js +146 -0
  43. package/build/manager/components/authentication/control.js.map +1 -0
  44. package/build/manager/components/authentication/index.d.ts +4 -0
  45. package/build/manager/components/authentication/index.d.ts.map +1 -0
  46. package/build/manager/components/authentication/index.js +4 -0
  47. package/build/manager/components/authentication/index.js.map +1 -0
  48. package/build/manager/components/authentication/types.d.ts +57 -0
  49. package/build/manager/components/authentication/types.d.ts.map +1 -0
  50. package/build/manager/components/authentication/types.js +2 -0
  51. package/build/manager/components/authentication/types.js.map +1 -0
  52. package/build/manager/components/index.d.ts +4 -0
  53. package/build/manager/components/index.d.ts.map +1 -0
  54. package/build/manager/components/index.js +3 -0
  55. package/build/manager/components/index.js.map +1 -0
  56. package/build/manager/components/tunnel-consumer.d.ts +4 -0
  57. package/build/manager/components/tunnel-consumer.d.ts.map +1 -0
  58. package/build/manager/components/tunnel-consumer.js +11 -0
  59. package/build/manager/components/tunnel-consumer.js.map +1 -0
  60. package/build/manager/components/types.d.ts +10 -0
  61. package/build/manager/components/types.d.ts.map +1 -0
  62. package/build/manager/components/types.js +2 -0
  63. package/build/manager/components/types.js.map +1 -0
  64. package/build/manager/errors.d.ts +6 -0
  65. package/build/manager/errors.d.ts.map +1 -0
  66. package/build/manager/errors.js +11 -0
  67. package/build/manager/errors.js.map +1 -0
  68. package/build/manager/index.d.ts +4 -0
  69. package/build/manager/index.d.ts.map +1 -0
  70. package/build/manager/index.js +4 -0
  71. package/build/manager/index.js.map +1 -0
  72. package/build/manager/modules.d.ts +2 -0
  73. package/build/manager/modules.d.ts.map +1 -0
  74. package/build/manager/modules.js +16 -0
  75. package/build/manager/modules.js.map +1 -0
  76. package/build/manager/plugins/basic-ed25519.d.ts +3 -0
  77. package/build/manager/plugins/basic-ed25519.d.ts.map +1 -0
  78. package/build/manager/plugins/basic-ed25519.js +33 -0
  79. package/build/manager/plugins/basic-ed25519.js.map +1 -0
  80. package/build/manager/plugins/exports.d.ts +6 -0
  81. package/build/manager/plugins/exports.d.ts.map +1 -0
  82. package/build/manager/plugins/exports.js +5 -0
  83. package/build/manager/plugins/exports.js.map +1 -0
  84. package/build/manager/plugins/index.d.ts +6 -0
  85. package/build/manager/plugins/index.d.ts.map +1 -0
  86. package/build/manager/plugins/index.js +9 -0
  87. package/build/manager/plugins/index.js.map +1 -0
  88. package/build/manager/plugins/re-captcha.d.ts +3 -0
  89. package/build/manager/plugins/re-captcha.d.ts.map +1 -0
  90. package/build/manager/plugins/re-captcha.js +26 -0
  91. package/build/manager/plugins/re-captcha.js.map +1 -0
  92. package/build/manager/plugins/tunnel/consts.d.ts +4 -0
  93. package/build/manager/plugins/tunnel/consts.d.ts.map +1 -0
  94. package/build/manager/plugins/tunnel/consts.js +9 -0
  95. package/build/manager/plugins/tunnel/consts.js.map +1 -0
  96. package/build/manager/plugins/tunnel/index.d.ts +4 -0
  97. package/build/manager/plugins/tunnel/index.d.ts.map +1 -0
  98. package/build/manager/plugins/tunnel/index.js +3 -0
  99. package/build/manager/plugins/tunnel/index.js.map +1 -0
  100. package/build/manager/plugins/tunnel/types.d.ts +13 -0
  101. package/build/manager/plugins/tunnel/types.d.ts.map +1 -0
  102. package/build/manager/plugins/tunnel/types.js +2 -0
  103. package/build/manager/plugins/tunnel/types.js.map +1 -0
  104. package/build/manager/plugins/tunnel/wallet.d.ts +4 -0
  105. package/build/manager/plugins/tunnel/wallet.d.ts.map +1 -0
  106. package/build/manager/plugins/tunnel/wallet.js +32 -0
  107. package/build/manager/plugins/tunnel/wallet.js.map +1 -0
  108. package/build/manager/plugins/tunnel-consumer.d.ts +3 -0
  109. package/build/manager/plugins/tunnel-consumer.d.ts.map +1 -0
  110. package/build/manager/plugins/tunnel-consumer.js +81 -0
  111. package/build/manager/plugins/tunnel-consumer.js.map +1 -0
  112. package/build/manager/plugins/types.d.ts +11 -0
  113. package/build/manager/plugins/types.d.ts.map +1 -0
  114. package/build/manager/plugins/types.js +2 -0
  115. package/build/manager/plugins/types.js.map +1 -0
  116. package/build/modules.d.ts +4 -0
  117. package/build/modules.d.ts.map +1 -0
  118. package/build/modules.js +9 -0
  119. package/build/modules.js.map +1 -0
  120. package/build/service.d.ts +6 -0
  121. package/build/service.d.ts.map +1 -0
  122. package/build/service.js +71 -0
  123. package/build/service.js.map +1 -0
  124. package/build/types.d.ts +13 -0
  125. package/build/types.d.ts.map +1 -0
  126. package/build/types.js +2 -0
  127. package/build/types.js.map +1 -0
  128. package/package.json +79 -0
  129. package/src/components/dispatcher/component.tsx +84 -0
  130. package/src/components/dispatcher/index.ts +3 -0
  131. package/src/components/dispatcher/types.ts +24 -0
  132. package/src/components/index.ts +2 -0
  133. package/src/consts.ts +6 -0
  134. package/src/helper.ts +39 -0
  135. package/src/index.ts +7 -0
  136. package/src/manager/README.md +463 -0
  137. package/src/manager/components/authentication/component.tsx +34 -0
  138. package/src/manager/components/authentication/consts.ts +2 -0
  139. package/src/manager/components/authentication/control.ts +193 -0
  140. package/src/manager/components/authentication/index.ts +4 -0
  141. package/src/manager/components/authentication/types.ts +74 -0
  142. package/src/manager/components/index.ts +4 -0
  143. package/src/manager/components/tunnel-consumer.tsx +15 -0
  144. package/src/manager/components/types.ts +11 -0
  145. package/src/manager/errors.ts +14 -0
  146. package/src/manager/index.ts +4 -0
  147. package/src/manager/modules.ts +18 -0
  148. package/src/manager/plugins/basic-ed25519.tsx +42 -0
  149. package/src/manager/plugins/exports.ts +5 -0
  150. package/src/manager/plugins/index.ts +13 -0
  151. package/src/manager/plugins/re-captcha.tsx +31 -0
  152. package/src/manager/plugins/tunnel/consts.ts +12 -0
  153. package/src/manager/plugins/tunnel/index.ts +5 -0
  154. package/src/manager/plugins/tunnel/types.ts +15 -0
  155. package/src/manager/plugins/tunnel/wallet.ts +43 -0
  156. package/src/manager/plugins/tunnel-consumer.tsx +100 -0
  157. package/src/manager/plugins/types.ts +14 -0
  158. package/src/modules.ts +13 -0
  159. package/src/service.ts +106 -0
  160. package/src/types.ts +15 -0
  161. package/tsconfig.json +15 -0
  162. 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,4 @@
1
+
2
+ export * from './component.js'
3
+ export * from './control.js'
4
+ export * from './types.js'
@@ -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,4 @@
1
+
2
+ export * from './authentication/index.js'
3
+ export * from './tunnel-consumer.js'
4
+ export type * from './types.js'
@@ -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,4 @@
1
+
2
+ export * from './plugins/index.js'
3
+ export * from './components/index.js'
4
+ export * from './errors.js'
@@ -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,5 @@
1
+ export type * from './types.js'
2
+ export * from './tunnel/index.js'
3
+ export * from './basic-ed25519.js'
4
+ export * from './re-captcha.js'
5
+ export * from './tunnel-consumer.js'
@@ -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,5 @@
1
+
2
+ export type * from './types.js'
3
+ export * from './consts.js'
4
+ export * from './wallet.js'
5
+
@@ -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
+ }