react-native-dpop 0.3.0 → 1.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-dpop",
3
- "version": "0.3.0",
3
+ "version": "1.0.0",
4
4
  "description": "React Native library for DPoP proof generation and key management.",
5
5
  "keywords": [
6
6
  "android",
@@ -18,6 +18,7 @@
18
18
  "license": "MIT",
19
19
  "author": "Pedro Cirilo <phscirilo123@gmail.com> (https://github.com/Cirilord)",
20
20
  "main": "./lib/module/index.js",
21
+ "react-native": "./src/index.tsx",
21
22
  "types": "./lib/typescript/src/index.d.ts",
22
23
  "exports": {
23
24
  ".": {
@@ -47,8 +48,8 @@
47
48
  "!**/.*"
48
49
  ],
49
50
  "scripts": {
50
- "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
51
- "example": "yarn workspace react-native-dpop-example",
51
+ "clean": "del-cli android/build examples/v0.83/android/build examples/v0.83/android/app/build examples/v0.83/ios/build lib",
52
+ "example": "yarn workspace react-native-dpop-example-v0.83",
52
53
  "lint": "eslint \"**/*.{js,ts,tsx}\"",
53
54
  "prepare": "bob build",
54
55
  "release": "release-it",
@@ -71,12 +72,14 @@
71
72
  "@react-native/babel-preset": "0.83.0",
72
73
  "@react-native/eslint-config": "0.83.0",
73
74
  "@release-it/conventional-changelog": "^10.0.1",
75
+ "@tsconfig/strictest": "^2.0.8",
74
76
  "@types/jest": "^29.5.14",
75
77
  "@types/react": "^19.2.0",
76
78
  "commitlint": "^19.8.1",
77
79
  "del-cli": "^6.0.0",
78
80
  "eslint": "^9.35.0",
79
81
  "eslint-config-prettier": "^10.1.8",
82
+ "eslint-plugin-import": "^2.32.0",
80
83
  "eslint-plugin-prettier": "^5.5.4",
81
84
  "jest": "^29.7.0",
82
85
  "lefthook": "^2.0.3",
@@ -89,7 +92,8 @@
89
92
  "typescript": "^5.9.2"
90
93
  },
91
94
  "workspaces": [
92
- "example"
95
+ "examples/v0.75",
96
+ "examples/v0.83"
93
97
  ],
94
98
  "packageManager": "yarn@4.11.0",
95
99
  "react-native-builder-bob": {
@@ -4,16 +4,7 @@ import type { UnsafeObject } from 'react-native/Libraries/Types/CodegenTypes';
4
4
 
5
5
  export interface Spec extends TurboModule {
6
6
  assertHardwareBacked(alias: string | null): Promise<void>;
7
- calculateThumbprint(alias: string | null): Promise<string>;
8
7
  deleteKeyPair(alias: string | null): Promise<void>;
9
- getKeyInfo(alias: string | null): Promise<UnsafeObject>;
10
- getPublicKeyDer(alias: string | null): Promise<string>;
11
- getPublicKeyJwk(alias: string | null): Promise<UnsafeObject>;
12
- getPublicKeyRaw(alias: string | null): Promise<string>;
13
- hasKeyPair(alias: string | null): Promise<boolean>;
14
- isBoundToAlias(proof: string, alias: string | null): Promise<boolean>;
15
- rotateKeyPair(alias: string | null): Promise<void>;
16
- signWithDpopPrivateKey(payload: string, alias: string | null): Promise<string>;
17
8
  generateProof(
18
9
  htu: string,
19
10
  htm: string,
@@ -23,11 +14,22 @@ export interface Spec extends TurboModule {
23
14
  kid: string | null,
24
15
  jti: string | null,
25
16
  iat: number | null,
26
- alias: string | null
17
+ alias: string | null,
18
+ requireHardwareBacked: boolean
27
19
  ): Promise<UnsafeObject>;
20
+ getKeyInfo(alias: string | null): Promise<UnsafeObject>;
21
+ getPublicKeyDer(alias: string | null): Promise<string>;
22
+ getPublicKeyJwk(alias: string | null): Promise<UnsafeObject>;
23
+ getPublicKeyRaw(alias: string | null): Promise<string>;
24
+ getPublicKeyThumbprint(alias: string | null): Promise<string>;
25
+ hasKeyPair(alias: string | null): Promise<boolean>;
26
+ isBoundToAlias(proof: string, alias: string | null): Promise<boolean>;
27
+ rotateKeyPair(alias: string | null): Promise<void>;
28
+ signWithDPoPPrivateKey(payload: string, alias: string | null): Promise<string>;
28
29
  }
29
30
 
30
- const nativeDpopModule =
31
- TurboModuleRegistry.get<Spec>('ReactNativeDPoP') ?? (NativeModules.ReactNativeDPoP as Spec | undefined);
31
+ const nativeDPoPModule =
32
+ // eslint-disable-next-line dot-notation -- required by noPropertyAccessFromIndexSignature from @tsconfig/strictest
33
+ TurboModuleRegistry.get<Spec>('ReactNativeDPoP') ?? (NativeModules['ReactNativeDPoP'] as Spec | undefined);
32
34
 
33
- export default nativeDpopModule as Spec;
35
+ export default nativeDPoPModule as Spec;
package/src/index.tsx CHANGED
@@ -3,8 +3,8 @@ import NativeReactNativeDPoP from './NativeReactNativeDPoP';
3
3
  type AdditionalClaims = Record<string, unknown>;
4
4
 
5
5
  export type PublicJwk = {
6
- kty: 'EC';
7
6
  crv: 'P-256';
7
+ kty: 'EC';
8
8
  x: string;
9
9
  y: string;
10
10
  };
@@ -12,50 +12,60 @@ export type PublicJwk = {
12
12
  export type PublicKeyFormat = 'JWK' | 'DER' | 'RAW';
13
13
 
14
14
  export type SecureHardwareFallbackReason = 'UNAVAILABLE' | 'PROVIDER_ERROR' | 'POLICY_REJECTED' | 'UNKNOWN';
15
+ export type AndroidSecurityLevelName = 'SOFTWARE' | 'TRUSTED_ENVIRONMENT' | 'STRONGBOX';
16
+ export type IOSSecurityLevelName = 'SOFTWARE' | 'SECURE_ENCLAVE';
15
17
 
16
18
  export type DPoPKeyInfo = {
17
- alias: string;
18
- hasKeyPair: boolean;
19
19
  algorithm?: string;
20
+ alias: string;
20
21
  curve?: string;
21
- insideSecureHardware?: boolean;
22
22
  hardware?: {
23
23
  android?: {
24
+ securityLevel?: number;
25
+ securityLevelName?: AndroidSecurityLevelName;
24
26
  strongBoxAvailable: boolean;
25
27
  strongBoxBacked: boolean;
26
- securityLevel?: number;
27
28
  strongBoxFallbackReason?: SecureHardwareFallbackReason | null;
28
29
  };
29
30
  ios?: {
30
31
  secureEnclaveAvailable: boolean;
31
32
  secureEnclaveBacked: boolean;
32
- securityLevel?: number | null;
33
33
  secureEnclaveFallbackReason?: SecureHardwareFallbackReason | null;
34
+ securityLevel?: number | null;
35
+ securityLevelName?: IOSSecurityLevelName;
34
36
  };
35
37
  };
38
+ hasKeyPair: boolean;
39
+ insideSecureHardware?: boolean;
36
40
  };
37
41
 
38
42
  export type GenerateProofInput = {
39
- htu: string;
40
- htm: string;
41
- nonce?: string;
42
43
  accessToken?: string;
43
44
  additional?: AdditionalClaims;
44
- kid?: string;
45
- jti?: string;
46
- iat?: number;
47
45
  alias?: string;
46
+ htm: string;
47
+ htu: string;
48
+ iat?: number;
49
+ jti?: string;
50
+ kid?: string;
51
+ nonce?: string;
52
+ requireHardwareBacked?: boolean;
53
+ };
54
+
55
+ export type DPoPHeaders = {
56
+ Authorization?: string;
57
+ DPoP: string;
48
58
  };
49
59
 
50
60
  export type DPoPProofContext = {
51
- htu: string;
52
- htm: string;
53
- nonce: string | null;
54
- ath: string | null;
55
61
  additional: AdditionalClaims | null;
56
- kid: string | null;
57
- jti: string;
62
+ ath: string | null;
63
+ htm: string;
64
+ htu: string;
58
65
  iat: number;
66
+ jti: string;
67
+ kid: string | null;
68
+ nonce: string | null;
59
69
  };
60
70
 
61
71
  type GenerateProofResult = {
@@ -64,8 +74,8 @@ type GenerateProofResult = {
64
74
  };
65
75
 
66
76
  export class DPoP {
77
+ public readonly alias: string | undefined;
67
78
  public readonly proof: string;
68
- public readonly alias?: string;
69
79
  public readonly proofContext: DPoPProofContext;
70
80
 
71
81
  private constructor(proof: string, proofContext: DPoPProofContext, alias?: string) {
@@ -74,27 +84,21 @@ export class DPoP {
74
84
  this.alias = alias;
75
85
  }
76
86
 
77
- public async calculateThumbprint(): Promise<string> {
78
- return NativeReactNativeDPoP.calculateThumbprint(this.alias ?? null);
87
+ public static async assertHardwareBacked(alias?: string): Promise<void> {
88
+ await NativeReactNativeDPoP.assertHardwareBacked(alias ?? null);
79
89
  }
80
90
 
81
- public async getPublicKey(format: PublicKeyFormat): Promise<PublicJwk | string> {
82
- if (format === 'DER') {
83
- return NativeReactNativeDPoP.getPublicKeyDer(this.alias ?? null);
84
- }
85
- if (format === 'RAW') {
86
- return NativeReactNativeDPoP.getPublicKeyRaw(this.alias ?? null);
87
- }
88
-
89
- return NativeReactNativeDPoP.getPublicKeyJwk(this.alias ?? null) as Promise<PublicJwk>;
90
- }
91
+ public static async buildDPoPHeaders(input: GenerateProofInput): Promise<DPoPHeaders> {
92
+ const dPoP = await DPoP.generateProof(input);
91
93
 
92
- public async signWithDpopPrivateKey(payload: string): Promise<string> {
93
- return NativeReactNativeDPoP.signWithDpopPrivateKey(payload, this.alias ?? null);
94
+ return {
95
+ DPoP: dPoP.proof,
96
+ ...(input.accessToken ? { Authorization: `DPoP ${input.accessToken}` } : {}),
97
+ };
94
98
  }
95
99
 
96
- public async isBoundToAlias(alias?: string): Promise<boolean> {
97
- return NativeReactNativeDPoP.isBoundToAlias(this.proof, alias ?? this.alias ?? null);
100
+ public static async deleteKeyPair(alias?: string): Promise<void> {
101
+ await NativeReactNativeDPoP.deleteKeyPair(alias ?? null);
98
102
  }
99
103
 
100
104
  public static async generateProof(input: GenerateProofInput): Promise<DPoP> {
@@ -106,21 +110,15 @@ export class DPoP {
106
110
  input.additional ?? null,
107
111
  input.kid ?? null,
108
112
  input.jti ?? null,
109
- input.iat ?? null,
110
- input.alias ?? null
113
+ // RN 0.75 Android bridge can crash when a nullable Double arrives as null.
114
+ input.iat ?? Math.floor(Date.now() / 1000),
115
+ input.alias ?? null,
116
+ input.requireHardwareBacked ?? false
111
117
  )) as GenerateProofResult;
112
118
 
113
119
  return new DPoP(result.proof, result.proofContext, input.alias);
114
120
  }
115
121
 
116
- public static async assertHardwareBacked(alias?: string): Promise<void> {
117
- await NativeReactNativeDPoP.assertHardwareBacked(alias ?? null);
118
- }
119
-
120
- public static async deleteKeyPair(alias?: string): Promise<void> {
121
- await NativeReactNativeDPoP.deleteKeyPair(alias ?? null);
122
- }
123
-
124
122
  public static async getKeyInfo(alias?: string): Promise<DPoPKeyInfo> {
125
123
  return NativeReactNativeDPoP.getKeyInfo(alias ?? null) as Promise<DPoPKeyInfo>;
126
124
  }
@@ -132,4 +130,27 @@ export class DPoP {
132
130
  public static async rotateKeyPair(alias?: string): Promise<void> {
133
131
  await NativeReactNativeDPoP.rotateKeyPair(alias ?? null);
134
132
  }
133
+
134
+ public async getPublicKey(format: PublicKeyFormat): Promise<PublicJwk | string> {
135
+ if (format === 'DER') {
136
+ return NativeReactNativeDPoP.getPublicKeyDer(this.alias ?? null);
137
+ }
138
+ if (format === 'RAW') {
139
+ return NativeReactNativeDPoP.getPublicKeyRaw(this.alias ?? null);
140
+ }
141
+
142
+ return NativeReactNativeDPoP.getPublicKeyJwk(this.alias ?? null) as Promise<PublicJwk>;
143
+ }
144
+
145
+ public async getPublicKeyThumbprint(): Promise<string> {
146
+ return NativeReactNativeDPoP.getPublicKeyThumbprint(this.alias ?? null);
147
+ }
148
+
149
+ public async isBoundToAlias(alias?: string): Promise<boolean> {
150
+ return NativeReactNativeDPoP.isBoundToAlias(this.proof, alias ?? this.alias ?? null);
151
+ }
152
+
153
+ public async signWithDPoPPrivateKey(payload: string): Promise<string> {
154
+ return NativeReactNativeDPoP.signWithDPoPPrivateKey(payload, this.alias ?? null);
155
+ }
135
156
  }