@pax2pay/model-banking 0.1.508 → 0.1.510

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 (99) hide show
  1. package/Card/Creatable.ts +10 -8
  2. package/Card/Restriction/Merchant.ts +108 -0
  3. package/Card/Restriction/index.ts +28 -0
  4. package/Card/index.ts +17 -8
  5. package/User/Access/Permission.ts +47 -0
  6. package/User/Access/index.ts +19 -0
  7. package/User/Identity.ts +32 -0
  8. package/User/JWT/Payload.ts +56 -0
  9. package/User/JWT/Signer.ts +39 -0
  10. package/User/JWT/index.ts +57 -0
  11. package/User/Me.ts +15 -0
  12. package/User/Password.ts +43 -0
  13. package/User/index.ts +68 -0
  14. package/dist/cjs/Card/Creatable.d.ts +4 -0
  15. package/dist/cjs/Card/Creatable.js +9 -8
  16. package/dist/cjs/Card/Creatable.js.map +1 -1
  17. package/dist/cjs/Card/Restriction/Merchant.d.ts +16 -0
  18. package/dist/cjs/Card/Restriction/Merchant.js +101 -0
  19. package/dist/cjs/Card/Restriction/Merchant.js.map +1 -0
  20. package/dist/cjs/Card/Restriction/index.d.ts +12 -0
  21. package/dist/cjs/Card/Restriction/index.js +26 -0
  22. package/dist/cjs/Card/Restriction/index.js.map +1 -0
  23. package/dist/cjs/Card/index.d.ts +7 -2
  24. package/dist/cjs/Card/index.js +16 -8
  25. package/dist/cjs/Card/index.js.map +1 -1
  26. package/dist/cjs/User/Access/Permission.d.ts +20 -0
  27. package/dist/cjs/User/Access/Permission.js +47 -0
  28. package/dist/cjs/User/Access/Permission.js.map +1 -0
  29. package/dist/cjs/User/Access/index.d.ts +11 -0
  30. package/dist/cjs/User/Access/index.js +16 -0
  31. package/dist/cjs/User/Access/index.js.map +1 -0
  32. package/dist/cjs/User/Identity.d.ts +16 -0
  33. package/dist/cjs/User/Identity.js +32 -0
  34. package/dist/cjs/User/Identity.js.map +1 -0
  35. package/dist/cjs/User/JWT/Payload.d.ts +43 -0
  36. package/dist/cjs/User/JWT/Payload.js +42 -0
  37. package/dist/cjs/User/JWT/Payload.js.map +1 -0
  38. package/dist/cjs/User/JWT/Signer.d.ts +23 -0
  39. package/dist/cjs/User/JWT/Signer.js +39 -0
  40. package/dist/cjs/User/JWT/Signer.js.map +1 -0
  41. package/dist/cjs/User/JWT/index.d.ts +25 -0
  42. package/dist/cjs/User/JWT/index.js +59 -0
  43. package/dist/cjs/User/JWT/index.js.map +1 -0
  44. package/dist/cjs/User/Me.d.ts +9 -0
  45. package/dist/cjs/User/Me.js +14 -0
  46. package/dist/cjs/User/Me.js.map +1 -0
  47. package/dist/cjs/User/Password.d.ts +20 -0
  48. package/dist/cjs/User/Password.js +44 -0
  49. package/dist/cjs/User/Password.js.map +1 -0
  50. package/dist/cjs/User/index.d.ts +46 -0
  51. package/dist/cjs/User/index.js +54 -0
  52. package/dist/cjs/User/index.js.map +1 -0
  53. package/dist/cjs/pax2pay.d.ts +2 -1
  54. package/dist/cjs/pax2pay.js +5 -3
  55. package/dist/cjs/pax2pay.js.map +1 -1
  56. package/dist/mjs/Card/Creatable.d.ts +4 -0
  57. package/dist/mjs/Card/Creatable.js +9 -8
  58. package/dist/mjs/Card/Creatable.js.map +1 -1
  59. package/dist/mjs/Card/Restriction/Merchant.d.ts +16 -0
  60. package/dist/mjs/Card/Restriction/Merchant.js +98 -0
  61. package/dist/mjs/Card/Restriction/Merchant.js.map +1 -0
  62. package/dist/mjs/Card/Restriction/index.d.ts +12 -0
  63. package/dist/mjs/Card/Restriction/index.js +23 -0
  64. package/dist/mjs/Card/Restriction/index.js.map +1 -0
  65. package/dist/mjs/Card/index.d.ts +7 -2
  66. package/dist/mjs/Card/index.js +16 -8
  67. package/dist/mjs/Card/index.js.map +1 -1
  68. package/dist/mjs/User/Access/Permission.d.ts +20 -0
  69. package/dist/mjs/User/Access/Permission.js +44 -0
  70. package/dist/mjs/User/Access/Permission.js.map +1 -0
  71. package/dist/mjs/User/Access/index.d.ts +11 -0
  72. package/dist/mjs/User/Access/index.js +13 -0
  73. package/dist/mjs/User/Access/index.js.map +1 -0
  74. package/dist/mjs/User/Identity.d.ts +16 -0
  75. package/dist/mjs/User/Identity.js +28 -0
  76. package/dist/mjs/User/Identity.js.map +1 -0
  77. package/dist/mjs/User/JWT/Payload.d.ts +43 -0
  78. package/dist/mjs/User/JWT/Payload.js +39 -0
  79. package/dist/mjs/User/JWT/Payload.js.map +1 -0
  80. package/dist/mjs/User/JWT/Signer.d.ts +23 -0
  81. package/dist/mjs/User/JWT/Signer.js +35 -0
  82. package/dist/mjs/User/JWT/Signer.js.map +1 -0
  83. package/dist/mjs/User/JWT/index.d.ts +25 -0
  84. package/dist/mjs/User/JWT/index.js +55 -0
  85. package/dist/mjs/User/JWT/index.js.map +1 -0
  86. package/dist/mjs/User/Me.d.ts +9 -0
  87. package/dist/mjs/User/Me.js +11 -0
  88. package/dist/mjs/User/Me.js.map +1 -0
  89. package/dist/mjs/User/Password.d.ts +20 -0
  90. package/dist/mjs/User/Password.js +41 -0
  91. package/dist/mjs/User/Password.js.map +1 -0
  92. package/dist/mjs/User/index.d.ts +46 -0
  93. package/dist/mjs/User/index.js +51 -0
  94. package/dist/mjs/User/index.js.map +1 -0
  95. package/dist/mjs/pax2pay.d.ts +2 -1
  96. package/dist/mjs/pax2pay.js +2 -1
  97. package/dist/mjs/pax2pay.js.map +1 -1
  98. package/package.json +1 -1
  99. package/pax2pay.ts +2 -1
package/Card/Creatable.ts CHANGED
@@ -8,6 +8,7 @@ import { type as ruleType } from "../Rule/type"
8
8
  import { Expiry } from "./Expiry"
9
9
  import { Meta } from "./Meta"
10
10
  import { Preset } from "./Preset"
11
+ import { Restriction } from "./Restriction"
11
12
 
12
13
  export interface Creatable {
13
14
  account: string
@@ -21,6 +22,7 @@ export interface Creatable {
21
22
  rules?: Rule[]
22
23
  meta?: Meta
23
24
  key?: isoly.Date | string
25
+ restricted?: { to?: Restriction }
24
26
  }
25
27
 
26
28
  export namespace Creatable {
@@ -28,24 +30,19 @@ export namespace Creatable {
28
30
  account: isly.string(),
29
31
  number: isly.string().optional(),
30
32
  preset: Preset.type,
31
- details: isly.object({
32
- expiry: Expiry.type,
33
- holder: isly.string(),
34
- }),
33
+ details: isly.object({ expiry: Expiry.type, holder: isly.string() }),
35
34
  limit: isly.tuple(isly.fromIs("isoly.Currency", isoly.Currency.is), isly.number()),
36
35
  rules: ruleType.array().optional(),
37
36
  meta: isly.fromIs("Card.Meta", Meta.is).optional(),
38
37
  key: isly.string().optional(),
38
+ restricted: isly.object<Required<Creatable>["restricted"]>({ to: Restriction.type.optional() }).optional(),
39
39
  })
40
40
  export const type2 = isly2.object<Creatable>({
41
41
  account: isly2.string().rename("Account").describe("The account id the card was created on."),
42
42
  number: isly2.string().optional().rename("Number").describe("The card identifier of the user of the api."),
43
43
  preset: Preset.type2,
44
44
  details: isly2
45
- .object({
46
- expiry: Expiry.type2,
47
- holder: isly2.string(),
48
- })
45
+ .object({ expiry: Expiry.type2, holder: isly2.string() })
49
46
  .rename("Details")
50
47
  .describe("The card details, the information that will be displayed on the card."),
51
48
  limit: isly2
@@ -60,5 +57,10 @@ export namespace Creatable {
60
57
  .describe("Card rules that applies to authorizations made with the card."),
61
58
  meta: isly2.from("Meta", Meta.is).optional(),
62
59
  key: isly2.string().optional(),
60
+ restricted: isly2
61
+ .object<Required<Creatable>["restricted"]>({ to: Restriction.type2.optional() })
62
+ .optional()
63
+ .rename("Restrictions")
64
+ .describe("Set of restrictions that apply to the card."),
63
65
  })
64
66
  }
@@ -0,0 +1,108 @@
1
+ import { isly } from "isly"
2
+ import { isly as isly2 } from "isly2"
3
+ import type { Transaction } from "../../Transaction"
4
+
5
+ export type Merchant = typeof Merchant.values[number]
6
+ export namespace Merchant {
7
+ export const values = [
8
+ "united airlines",
9
+ "american air",
10
+ "british airways",
11
+ "air france",
12
+ "lufthansa",
13
+ "qantas",
14
+ "alitalia",
15
+ "saudi air",
16
+ "sas",
17
+ "air india",
18
+ "air algerie",
19
+ "emirates",
20
+ "air malta",
21
+ "etihad air",
22
+ "tap",
23
+ "avianca",
24
+ "gulf air",
25
+ "aer lingus",
26
+ "turkish air",
27
+ "royal air maroc",
28
+ "tunis air",
29
+ "austrian air",
30
+ "flydubai",
31
+ "cebu air",
32
+ "thai airways",
33
+ "china airlines",
34
+ "malaysia airlines",
35
+ "iberia",
36
+ "qatar air",
37
+ "ana air",
38
+ "jet blue",
39
+ "middle east airlines",
40
+ "lot polish airlines",
41
+ "norwegian air",
42
+ "air arabia",
43
+ "easyjet",
44
+ "ryanair",
45
+ "air china",
46
+ "ethiopian airlines",
47
+ "wizz air",
48
+ ] as const
49
+ export const type = isly.string<Merchant>(values)
50
+ export const type2 = isly2.string<Merchant>("value", ...values)
51
+ export type Checker = (
52
+ merchant: Merchant,
53
+ transaction: Transaction.Creatable.CardTransaction | Transaction.PreTransaction.Authorization
54
+ ) => boolean
55
+ export type Attribute = { mccs: string[]; checkers?: Checker[] }
56
+ export const attributes: Record<Merchant, Attribute> = {
57
+ "united airlines": { mccs: ["3000"] },
58
+ "american air": { mccs: ["3001"] },
59
+ "british airways": { mccs: ["3005"] },
60
+ "air france": { mccs: ["3007"] },
61
+ lufthansa: { mccs: ["3008"] },
62
+ qantas: { mccs: ["3012"] },
63
+ alitalia: { mccs: ["3013"] },
64
+ "saudi air": { mccs: ["3014"] },
65
+ sas: { mccs: ["3016"] },
66
+ "air india": { mccs: ["3020"] },
67
+ "air algerie": { mccs: ["3021"] },
68
+ emirates: { mccs: ["3026"] },
69
+ "air malta": { mccs: ["3028"] },
70
+ "etihad air": { mccs: ["3034"] },
71
+ tap: { mccs: ["3035"] },
72
+ avianca: { mccs: ["3039"] },
73
+ "gulf air": { mccs: ["3040"] },
74
+ "aer lingus": { mccs: ["3043"] },
75
+ "turkish air": { mccs: ["3047"] },
76
+ "royal air maroc": { mccs: ["3048"] },
77
+ "tunis air": { mccs: ["3049"] },
78
+ "austrian air": { mccs: ["3051"] },
79
+ flydubai: { mccs: ["3070"] },
80
+ "cebu air": { mccs: ["3072"] },
81
+ "thai airways": { mccs: ["3077"] },
82
+ "china airlines": { mccs: ["3078"] },
83
+ "malaysia airlines": { mccs: ["3100"] },
84
+ iberia: { mccs: ["3102"] },
85
+ "qatar air": { mccs: ["3136"] },
86
+ "ana air": { mccs: ["3161"] },
87
+ "jet blue": { mccs: ["3174"] },
88
+ "middle east airlines": { mccs: ["3175"] },
89
+ "lot polish airlines": { mccs: ["3182"] },
90
+ "norwegian air": { mccs: ["3211"] },
91
+ "air arabia": { mccs: ["3236"] },
92
+ easyjet: { mccs: ["3245"] },
93
+ ryanair: { mccs: ["3246"] },
94
+ "air china": { mccs: ["3261"] },
95
+ "ethiopian airlines": { mccs: ["3294"] },
96
+ "wizz air": { mccs: ["3301"] },
97
+ }
98
+ export function check(
99
+ merchant: Merchant,
100
+ transaction: Transaction.Creatable.CardTransaction | Transaction.PreTransaction.Authorization
101
+ ): boolean {
102
+ const attribute = Merchant.attributes[merchant]
103
+ return (
104
+ attribute.mccs.some(mcc => transaction.counterpart.merchant.category == mcc) ||
105
+ (attribute.checkers ?? []).some(checker => checker(merchant, transaction))
106
+ )
107
+ }
108
+ }
@@ -0,0 +1,28 @@
1
+ import { isly } from "isly"
2
+ import { isly as isly2 } from "isly2"
3
+ import type { Transaction } from "../../Transaction"
4
+ import { Merchant as RestrictionsMerchant } from "./Merchant"
5
+
6
+ export interface Restriction {
7
+ merchants?: Restriction.Merchant[]
8
+ }
9
+ export namespace Restriction {
10
+ export import Merchant = RestrictionsMerchant
11
+ export const type = isly.object<Restriction>({ merchants: Merchant.type.array().optional() })
12
+ export const type2 = isly2.object<Restriction>({
13
+ merchants: Merchant.type2
14
+ .array()
15
+ .optional()
16
+ .rename("Merchants")
17
+ .describe("List of merchants that the card can be used with."),
18
+ })
19
+ export function check(
20
+ restrictions: Restriction,
21
+ transaction: Transaction.Creatable.CardTransaction | Transaction.PreTransaction.Authorization
22
+ ): boolean {
23
+ let result: boolean = true
24
+ if (restrictions.merchants?.length)
25
+ result = restrictions.merchants.some(merchant => Merchant.check(merchant, transaction))
26
+ return result
27
+ }
28
+ }
package/Card/index.ts CHANGED
@@ -12,6 +12,7 @@ import { Expiry as CardExpiry } from "./Expiry"
12
12
  import { Meta as CardMeta } from "./Meta"
13
13
  import { Operation as CardOperation } from "./Operation"
14
14
  import { Preset as CardPreset } from "./Preset"
15
+ import { Restriction as CardRestriction } from "./Restriction"
15
16
  import { Scheme as CardScheme } from "./Scheme"
16
17
  import { Stack as CardStack } from "./Stack"
17
18
 
@@ -38,8 +39,18 @@ export interface Card {
38
39
  history: CardOperation[]
39
40
  rules: Rule[]
40
41
  meta?: CardMeta
42
+ restricted?: { to?: CardRestriction }
41
43
  }
42
44
  export namespace Card {
45
+ export import Creatable = CardCreatable
46
+ export import Preset = CardPreset
47
+ export import Meta = CardMeta
48
+ export import Expiry = CardExpiry
49
+ export import Changeable = CardChangeable
50
+ export import Operation = CardOperation
51
+ export import Scheme = CardScheme
52
+ export import Stack = CardStack
53
+ export import Restriction = CardRestriction
43
54
  export const type = isly.object<Card>({
44
55
  id: isly.string(),
45
56
  number: isly.string().optional(),
@@ -63,6 +74,7 @@ export namespace Card {
63
74
  history: isly.array(CardOperation.type),
64
75
  rules: ruleType.array(),
65
76
  meta: isly.fromIs("Card.Meta", CardMeta.is).optional(),
77
+ restricted: isly.object<Required<Card>["restricted"]>({ to: CardRestriction.type.optional() }).optional(),
66
78
  })
67
79
  export const type2: isly2.Object<Card> = isly2
68
80
  .object<Card>({
@@ -92,17 +104,14 @@ export namespace Card {
92
104
  .rename("Rules")
93
105
  .describe("Card rules that applies to authorizations made with the card."),
94
106
  meta: isly2.from("Card.Meta", CardMeta.is).optional().rename("Meta").describe("Additional card information."),
107
+ restricted: isly2
108
+ .object<Required<Card>["restricted"]>({ to: CardRestriction.type2.optional() })
109
+ .optional()
110
+ .rename("Restrictions")
111
+ .describe("Set of restrictions that apply to the card."),
95
112
  })
96
113
  .rename("Card")
97
114
  .describe("Card information.")
98
- export import Creatable = CardCreatable
99
- export import Preset = CardPreset
100
- export import Meta = CardMeta
101
- export import Expiry = CardExpiry
102
- export import Changeable = CardChangeable
103
- export import Operation = CardOperation
104
- export import Scheme = CardScheme
105
- export import Stack = CardStack
106
115
  const csvMap: Record<string, (card: Card) => string | number | undefined> = {
107
116
  id: card => card.id,
108
117
  created: card => readableDate(card.created),
@@ -0,0 +1,47 @@
1
+ import { isly } from "isly"
2
+ import { typedly } from "typedly"
3
+
4
+ export type Permission = Partial<Record<Permission.Collection, Permission.Level>>
5
+ export namespace Permission {
6
+ export type Realm = Omit<Permission, "user">
7
+ export type Global = Pick<Permission, "user">
8
+ export function check(constraint: Permission, privilege: Permission): boolean {
9
+ return typedly.Object.entries(constraint).every(
10
+ ([collection, level]) =>
11
+ Permission.Level.get(privilege[collection]) >= Permission.Level.get(level) ||
12
+ Permission.Level.get(privilege["*"]) >= Permission.Level.get(level)
13
+ )
14
+ }
15
+ export type Level = typeof Level.values[number]
16
+ export namespace Level {
17
+ export const values = ["read", "write", "developer", "admin"] as const
18
+ export const type = isly.string(values)
19
+ export function get(level: Level | undefined): number {
20
+ return level ? value[level] : 0
21
+ }
22
+ export const value: Record<Level, number> = {
23
+ read: 1,
24
+ write: 2,
25
+ developer: 3,
26
+ admin: 4,
27
+ } as const
28
+ }
29
+ export type Collection = typeof Collection.values[number]
30
+ export namespace Collection {
31
+ export const values = [
32
+ "account",
33
+ "card",
34
+ "log",
35
+ "operation",
36
+ "organization",
37
+ "rule", // realm rules
38
+ "settlement",
39
+ "transaction",
40
+ "treasury",
41
+ "user",
42
+ "*",
43
+ ] as const
44
+ export const type = isly.string(values)
45
+ }
46
+ export const type = isly.record<Permission>(Collection.type, Level.type)
47
+ }
@@ -0,0 +1,19 @@
1
+ import { isly } from "isly"
2
+ import { Permission as AccessPermission } from "./Permission"
3
+
4
+ /** read < write < developer < admin */
5
+ export interface Access {
6
+ uk?: Access.Permission.Realm
7
+ eea?: Access.Permission.Realm
8
+ test?: Access.Permission.Realm
9
+ "*"?: Access.Permission.Global
10
+ }
11
+ export namespace Access {
12
+ export import Permission = AccessPermission
13
+ export const type = isly.object({
14
+ uk: Access.Permission.type.optional(),
15
+ eea: Access.Permission.type.optional(),
16
+ test: Access.Permission.type.optional(),
17
+ "*": isly.object({ user: Access.Permission.Level.type.optional() }).optional(),
18
+ })
19
+ }
@@ -0,0 +1,32 @@
1
+ import { gracely } from "gracely"
2
+ import { http } from "cloudly-http"
3
+ import { Realm } from "../Realm"
4
+ import { Access } from "./Access"
5
+ import { JWT } from "./JWT"
6
+
7
+ export class Identity {
8
+ get realm(): Realm {
9
+ return this.payload.realm
10
+ }
11
+ constructor(public readonly payload: JWT.Payload, private readonly jwt: string) {}
12
+
13
+ authenticate(constraint: Access.Permission | Access.Permission[]): Identity | gracely.Error {
14
+ let allowed: boolean
15
+ if (Array.isArray(constraint))
16
+ allowed = constraint.some(c => this.authenticate(c))
17
+ else
18
+ allowed = Access.Permission.check(constraint, this.payload.permission)
19
+ return allowed ? this : gracely.client.forbidden()
20
+ }
21
+
22
+ /** Key will default to production jwt verification key */
23
+ static async open(
24
+ authorization: string | undefined,
25
+ options: { whitelist?: JWT.Whitelist; key?: string }
26
+ ): Promise<Identity | gracely.Error> {
27
+ const jwt = authorization?.startsWith("Bearer ") ? authorization.replace("Bearer ", "") : undefined
28
+ const payload = jwt ? await JWT.open({ public: options.key }, options.whitelist).verify(jwt) : undefined
29
+ return jwt && payload ? new Identity(payload, jwt) : gracely.client.unauthorized()
30
+ }
31
+ }
32
+ export namespace Identity {}
@@ -0,0 +1,56 @@
1
+ import { authly } from "authly"
2
+ import { isly } from "isly"
3
+ import { Realm } from "../../Realm"
4
+ import { Access } from "../Access"
5
+
6
+ export type Payload = Payload.LongTerm | Payload.ShortTerm
7
+ export namespace Payload {
8
+ export interface Creatable {
9
+ sub: string
10
+ permission: Access.Permission
11
+ realm: Realm
12
+ }
13
+ export namespace Creatable {
14
+ export const type = isly.object<Creatable>({
15
+ sub: isly.string(),
16
+ permission: Access.Permission.type,
17
+ realm: Realm.type,
18
+ })
19
+ }
20
+ export interface Base extends authly.Payload {
21
+ aud: string
22
+ iat: number
23
+ iss: string
24
+ sub: string
25
+ permission: Access.Permission
26
+ realm: Realm
27
+ }
28
+ export namespace Base {
29
+ export const type = isly.object<Payload>({
30
+ aud: isly.string(),
31
+ iat: isly.number(),
32
+ iss: isly.string(),
33
+ sub: isly.string(),
34
+ permission: Access.Permission.type,
35
+ realm: Realm.type,
36
+ })
37
+ }
38
+ export interface LongTerm extends Base {
39
+ id: string
40
+ }
41
+ export namespace LongTerm {
42
+ export const type = Base.type.extend<LongTerm>({ id: isly.string() })
43
+ }
44
+ export interface ShortTerm extends Base {
45
+ exp: number
46
+ }
47
+ export namespace ShortTerm {
48
+ export const type = Base.type.extend<ShortTerm>({ exp: isly.number() })
49
+ }
50
+
51
+ export const configuration = {
52
+ aud: "https://banking.pax2pay.app",
53
+ iss: "pax2pay",
54
+ }
55
+ export const type = isly.union<Payload>(LongTerm.type, ShortTerm.type)
56
+ }
@@ -0,0 +1,39 @@
1
+ import { authly } from "authly"
2
+ import { Payload } from "./Payload"
3
+
4
+ export class Signer {
5
+ constructor(private readonly key?: { public?: string; private?: string }) {}
6
+
7
+ /** Duration in seconds */
8
+ async sign(data: Payload.Creatable, duration: number | "infinite" = 60 * 60 * 12): Promise<string | undefined> {
9
+ let result: string | undefined
10
+ if (duration === "infinite") {
11
+ const signer = Signer.create({ key: this.key })
12
+ result = await signer?.sign({ id: authly.Identifier.generate(16), ...data })
13
+ } else {
14
+ const signer = Signer.create({ key: this.key, duration })
15
+ result = await signer?.sign({ ...data })
16
+ }
17
+ return result
18
+ }
19
+
20
+ static open(key?: { public?: string; private?: string }): Signer {
21
+ return new this(key)
22
+ }
23
+ }
24
+ export namespace Signer {
25
+ export function create<T extends authly.Payload>(options: {
26
+ key?: { public?: string; private?: string }
27
+ duration?: number
28
+ }): authly.Issuer<T> | undefined {
29
+ const signer = authly.Issuer.create(
30
+ Payload.configuration.iss,
31
+ authly.Algorithm.RS256(options.key?.public, options.key?.private)
32
+ )
33
+ if (signer) {
34
+ signer.duration = options.duration
35
+ signer.audience = Payload.configuration.aud
36
+ }
37
+ return signer
38
+ }
39
+ }
@@ -0,0 +1,57 @@
1
+ import { authly } from "authly"
2
+ import { Realm } from "../../Realm"
3
+ import { Payload as JWTPayload } from "./Payload"
4
+ import { Signer as JWTSigner } from "./Signer"
5
+
6
+ export class JWT {
7
+ #verifier?: authly.Verifier<JWT.Payload>
8
+ private get verifier(): authly.Verifier<JWT.Payload> | undefined {
9
+ if (!this.#verifier && this.key?.public) {
10
+ const algorithm = authly.Algorithm.RS256(this.key.public)
11
+ this.#verifier = algorithm ? authly.Verifier.create(algorithm) : undefined
12
+ }
13
+ return this.#verifier
14
+ }
15
+ #signer?: JWTSigner
16
+ private get signer(): JWTSigner | undefined {
17
+ return this.key?.private ? (this.#signer ??= JWTSigner.open(this.key)) : undefined
18
+ }
19
+ get sign() {
20
+ return this.signer?.sign
21
+ }
22
+ private constructor(
23
+ private readonly key?: { public?: string; private?: string },
24
+ readonly whitelist?: JWT.Whitelist
25
+ ) {}
26
+
27
+ async verify(token: string): Promise<JWT.Payload | undefined> {
28
+ const verified = await this.verifier?.verify(token, JWT.Payload.configuration.aud)
29
+ delete verified?.token
30
+ return JWT.Payload.type.is(verified) &&
31
+ verified?.iss == JWT.Payload.configuration.iss &&
32
+ (verified.exp || (verified.id && this.whitelist?.[verified.realm]?.some(e => e.id === verified.id)))
33
+ ? verified
34
+ : undefined
35
+ }
36
+ async unpack(token: string): Promise<JWT.Payload | undefined> {
37
+ const unpacked = await JWT.unpack(token)
38
+ delete unpacked?.token
39
+ return unpacked
40
+ }
41
+
42
+ static open(key?: { private?: string; public?: string }, whitelist?: JWT.Whitelist): JWT {
43
+ return new this({ private: key?.private, public: key?.public ?? JWT.key }, whitelist)
44
+ }
45
+ }
46
+ export namespace JWT {
47
+ export import Signer = JWTSigner
48
+ export type Whitelist = Partial<Record<Realm, Payload.LongTerm[]>>
49
+ export async function unpack(token: string): Promise<JWT.Payload | undefined> {
50
+ const algorithm = authly.Algorithm.RS256(undefined)
51
+ const verifier = algorithm ? authly.Verifier.create<JWT.Payload>(algorithm) : undefined
52
+ return verifier?.unpack(token)
53
+ }
54
+ export import Payload = JWTPayload
55
+ export const key =
56
+ "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2W8CD2kpfS4QIRV2/rgm4NVvsvJsYNMHtnIl9ADvO3A81hAmRKvOAPVoXICe6+EuZ47jGjGL7f48GEoQuITfBPv/MosCDj1YhJ56ILDynCSd8FlxDrhv8pl5IquST7tcL6Hc6m+vuvoTLrFQ5QqNxv0a5eDd/YTrWv7SUuRfBEhYd/wMysGynN3QauHqy5ceBCt1nv1MJLGlSzczMRK7wjy1zi2g9NCHZBOoo1HXOpi727Xh+YXHc9EP2TN0oOXyxykv45nkGIDI0Qek3/pfkavClBffc1sEqA+rUx7YqRN9KGYxwLMLug+NOOh3ptqjfobXbR5fx/sUWhvcjUMTE1JreTrWYbGmVnjd/SeYSClfmGhdTBUfqnZbaABv0ruTXva18qRhP4y143vHMk/k8HzbuROTKAzrtEeLIjgwUgDcnE+JwDqcb8tKSGV6i++TiTldlSBCRTT4dK2hpHJje80b2abqtrbCkxbJlT98UsAAoiq2eW1X6lYmCfiGCJPkfswibQ2tPAKKNe/2xuHPsjx4FuXGmV0dbzmCwSIQoApXqOvKzoNFi6AaKIjxfNmiEigLwKpNrw08H0lVZbq/9MMxI3TzMTZjY9QmBKVLSGy3Z6IJqZpyK22lv7whJcllG0Qw8tv8+7wmC8SR3+4jpuxuFGZ+69CW+otx+CPMJjcCAwEAAQ=="
57
+ }
package/User/Me.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { isly } from "isly"
2
+ import { Realm } from "../Realm"
3
+
4
+ export interface Me {
5
+ email: string
6
+ password: string
7
+ realm: Realm
8
+ }
9
+ export namespace Me {
10
+ export const type = isly.object<Me>({
11
+ email: isly.string(),
12
+ password: isly.string(),
13
+ realm: Realm.type,
14
+ })
15
+ }
@@ -0,0 +1,43 @@
1
+ import { cryptly } from "cryptly"
2
+ import { gracely } from "gracely"
3
+ import { isoly } from "isoly"
4
+ import { isly } from "isly"
5
+
6
+ export interface Password {
7
+ hash: cryptly.Password.Hash
8
+ changed: isoly.DateTime
9
+ }
10
+ export namespace Password {
11
+ export interface Creatable {
12
+ new: string
13
+ repeat: string
14
+ }
15
+ export namespace Creatable {
16
+ export const type = isly.object<Creatable>({
17
+ new: isly.string(),
18
+ repeat: isly.string(),
19
+ })
20
+ }
21
+ export async function create(creatable: Creatable, pepper: string | undefined): Promise<Password | gracely.Error> {
22
+ let result: Awaited<ReturnType<typeof create>>
23
+ if (creatable.new !== creatable.repeat)
24
+ result = gracely.client.forbidden("The new password and the repeated password do not match.")
25
+ else if (!pepper)
26
+ result = gracely.server.backendFailure("The password cannot be created without a pepper.")
27
+ else
28
+ result = { hash: await hash(creatable.new, pepper), changed: isoly.DateTime.now() }
29
+ return result
30
+ }
31
+ export function salt(): string {
32
+ return cryptly.Base64.encode(cryptly.RandomValue.generate(new Uint8Array(64)).toString())
33
+ }
34
+ export async function hash(password: string, pepper: string): Promise<cryptly.Password.Hash> {
35
+ return cryptly.Password.hash(signer(pepper), password, salt())
36
+ }
37
+ export async function verify(password: string, hash: cryptly.Password.Hash, pepper: string): Promise<boolean> {
38
+ return cryptly.Password.verify(signer(pepper), hash, password)
39
+ }
40
+ function signer(pepper: string): { sign: (data: string) => Promise<string> } {
41
+ return cryptly.Signer.create("HMAC", "SHA-512", pepper)
42
+ }
43
+ }
package/User/index.ts ADDED
@@ -0,0 +1,68 @@
1
+ import { isoly } from "isoly"
2
+ import { isly } from "isly"
3
+ import { Realm } from "../Realm"
4
+ import { Access as UserAccess } from "./Access"
5
+ import { Identity as UserIdentity } from "./Identity"
6
+ import { JWT as UserJWT } from "./JWT"
7
+ import { Me as UserMe } from "./Me"
8
+ import { Password as UserPassword } from "./Password"
9
+
10
+ export interface User {
11
+ email: string
12
+ access: User.Access
13
+ changed: isoly.DateTime
14
+ created: isoly.DateTime
15
+ password: {
16
+ changed: isoly.DateTime
17
+ }
18
+ }
19
+ export namespace User {
20
+ export import Access = UserAccess
21
+ export import Identity = UserIdentity
22
+ export import JWT = UserJWT
23
+ export import Me = UserMe
24
+ export import Password = UserPassword
25
+ export function fromInvite(invite: Invite): User {
26
+ const now = isoly.DateTime.now()
27
+ return {
28
+ email: invite.email,
29
+ access: invite.access,
30
+ changed: now,
31
+ created: now,
32
+ password: {
33
+ changed: now,
34
+ },
35
+ }
36
+ }
37
+ export function toJWTPayloadCreatable(user: User, realm: Realm): User.JWT.Payload.Creatable {
38
+ return {
39
+ sub: user.email,
40
+ permission: { ...(user.access[realm] ?? {}), ...(user.access["*"] ?? {}) },
41
+ realm,
42
+ }
43
+ }
44
+ export interface Creatable {
45
+ invite: string
46
+ password: Password.Creatable
47
+ }
48
+ export namespace Creatable {
49
+ export const type = isly.object<Creatable>({
50
+ invite: isly.string(),
51
+ password: isly.object({ new: isly.string(), repeat: isly.string() }),
52
+ })
53
+ }
54
+ export interface Invite {
55
+ id: string
56
+ email: string
57
+ access: Access
58
+ }
59
+ export namespace Invite {
60
+ export interface Creatable {
61
+ email: string
62
+ access: Access
63
+ }
64
+ export namespace Creatable {
65
+ export const type = isly.object<Creatable>({ email: isly.string(), access: Access.type })
66
+ }
67
+ }
68
+ }
@@ -5,6 +5,7 @@ import type { Rule } from "../Rule";
5
5
  import { Expiry } from "./Expiry";
6
6
  import { Meta } from "./Meta";
7
7
  import { Preset } from "./Preset";
8
+ import { Restriction } from "./Restriction";
8
9
  export interface Creatable {
9
10
  account: string;
10
11
  number?: string;
@@ -17,6 +18,9 @@ export interface Creatable {
17
18
  rules?: Rule[];
18
19
  meta?: Meta;
19
20
  key?: isoly.Date | string;
21
+ restricted?: {
22
+ to?: Restriction;
23
+ };
20
24
  }
21
25
  export declare namespace Creatable {
22
26
  const type: import("isly/dist/cjs/object").IslyObject<Creatable, object>;