@pagopa/io-react-native-jwt 0.4.1

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 (113) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +145 -0
  3. package/android/build.gradle +97 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/java/com/pagopa/ioreactnativejwt/IoReactNativeJwtModule.kt +114 -0
  7. package/android/src/main/java/com/pagopa/ioreactnativejwt/IoReactNativeJwtPackage.kt +17 -0
  8. package/android/src/main/java/com/pagopa/ioreactnativejwt/Utils.kt +95 -0
  9. package/ios/ASN1DERParsing.swift +226 -0
  10. package/ios/IoReactNativeJwt-Bridging-Header.h +2 -0
  11. package/ios/IoReactNativeJwt.mm +28 -0
  12. package/ios/IoReactNativeJwt.swift +156 -0
  13. package/ios/IoReactNativeJwt.xcodeproj/project.pbxproj +283 -0
  14. package/lib/commonjs/algorithms.js +39 -0
  15. package/lib/commonjs/algorithms.js.map +1 -0
  16. package/lib/commonjs/hash.js +17 -0
  17. package/lib/commonjs/hash.js.map +1 -0
  18. package/lib/commonjs/index.js +194 -0
  19. package/lib/commonjs/index.js.map +1 -0
  20. package/lib/commonjs/jwt_claims_set.js +112 -0
  21. package/lib/commonjs/jwt_claims_set.js.map +1 -0
  22. package/lib/commonjs/produce.js +140 -0
  23. package/lib/commonjs/produce.js.map +1 -0
  24. package/lib/commonjs/sign.js +159 -0
  25. package/lib/commonjs/sign.js.map +1 -0
  26. package/lib/commonjs/types.js +2 -0
  27. package/lib/commonjs/types.js.map +1 -0
  28. package/lib/commonjs/unsecured.js +118 -0
  29. package/lib/commonjs/unsecured.js.map +1 -0
  30. package/lib/commonjs/utils/asn1.js +37 -0
  31. package/lib/commonjs/utils/asn1.js.map +1 -0
  32. package/lib/commonjs/utils/base64.js +43 -0
  33. package/lib/commonjs/utils/base64.js.map +1 -0
  34. package/lib/commonjs/utils/errors.js +214 -0
  35. package/lib/commonjs/utils/errors.js.map +1 -0
  36. package/lib/commonjs/utils/is_object.js +23 -0
  37. package/lib/commonjs/utils/is_object.js.map +1 -0
  38. package/lib/commonjs/utils/proxy.js +17 -0
  39. package/lib/commonjs/utils/proxy.js.map +1 -0
  40. package/lib/commonjs/utils/secs.js +61 -0
  41. package/lib/commonjs/utils/secs.js.map +1 -0
  42. package/lib/module/algorithms.js +29 -0
  43. package/lib/module/algorithms.js.map +1 -0
  44. package/lib/module/hash.js +11 -0
  45. package/lib/module/hash.js.map +1 -0
  46. package/lib/module/index.js +115 -0
  47. package/lib/module/index.js.map +1 -0
  48. package/lib/module/jwt_claims_set.js +103 -0
  49. package/lib/module/jwt_claims_set.js.map +1 -0
  50. package/lib/module/produce.js +131 -0
  51. package/lib/module/produce.js.map +1 -0
  52. package/lib/module/sign.js +153 -0
  53. package/lib/module/sign.js.map +1 -0
  54. package/lib/module/types.js +2 -0
  55. package/lib/module/types.js.map +1 -0
  56. package/lib/module/unsecured.js +110 -0
  57. package/lib/module/unsecured.js.map +1 -0
  58. package/lib/module/utils/asn1.js +31 -0
  59. package/lib/module/utils/asn1.js.map +1 -0
  60. package/lib/module/utils/base64.js +36 -0
  61. package/lib/module/utils/base64.js.map +1 -0
  62. package/lib/module/utils/errors.js +200 -0
  63. package/lib/module/utils/errors.js.map +1 -0
  64. package/lib/module/utils/is_object.js +17 -0
  65. package/lib/module/utils/is_object.js.map +1 -0
  66. package/lib/module/utils/proxy.js +10 -0
  67. package/lib/module/utils/proxy.js.map +1 -0
  68. package/lib/module/utils/secs.js +53 -0
  69. package/lib/module/utils/secs.js.map +1 -0
  70. package/lib/typescript/algorithms.d.ts +5 -0
  71. package/lib/typescript/algorithms.d.ts.map +1 -0
  72. package/lib/typescript/hash.d.ts +5 -0
  73. package/lib/typescript/hash.d.ts.map +1 -0
  74. package/lib/typescript/index.d.ts +93 -0
  75. package/lib/typescript/index.d.ts.map +1 -0
  76. package/lib/typescript/jwt_claims_set.d.ts +4 -0
  77. package/lib/typescript/jwt_claims_set.d.ts.map +1 -0
  78. package/lib/typescript/produce.d.ts +55 -0
  79. package/lib/typescript/produce.d.ts.map +1 -0
  80. package/lib/typescript/sign.d.ts +61 -0
  81. package/lib/typescript/sign.d.ts.map +1 -0
  82. package/lib/typescript/types.d.ts +172 -0
  83. package/lib/typescript/types.d.ts.map +1 -0
  84. package/lib/typescript/unsecured.d.ts +64 -0
  85. package/lib/typescript/unsecured.d.ts.map +1 -0
  86. package/lib/typescript/utils/asn1.d.ts +2 -0
  87. package/lib/typescript/utils/asn1.d.ts.map +1 -0
  88. package/lib/typescript/utils/base64.d.ts +4 -0
  89. package/lib/typescript/utils/base64.d.ts.map +1 -0
  90. package/lib/typescript/utils/errors.d.ts +157 -0
  91. package/lib/typescript/utils/errors.d.ts.map +1 -0
  92. package/lib/typescript/utils/is_object.d.ts +2 -0
  93. package/lib/typescript/utils/is_object.d.ts.map +1 -0
  94. package/lib/typescript/utils/proxy.d.ts +2 -0
  95. package/lib/typescript/utils/proxy.d.ts.map +1 -0
  96. package/lib/typescript/utils/secs.d.ts +4 -0
  97. package/lib/typescript/utils/secs.d.ts.map +1 -0
  98. package/package.json +170 -0
  99. package/pagopa-io-react-native-jwt.podspec +44 -0
  100. package/src/algorithms.ts +46 -0
  101. package/src/hash.ts +10 -0
  102. package/src/index.tsx +120 -0
  103. package/src/jwt_claims_set.ts +194 -0
  104. package/src/produce.ts +109 -0
  105. package/src/sign.ts +180 -0
  106. package/src/types.ts +203 -0
  107. package/src/unsecured.ts +127 -0
  108. package/src/utils/asn1.ts +36 -0
  109. package/src/utils/base64.ts +37 -0
  110. package/src/utils/errors.ts +206 -0
  111. package/src/utils/is_object.ts +20 -0
  112. package/src/utils/proxy.ts +15 -0
  113. package/src/utils/secs.ts +58 -0
package/package.json ADDED
@@ -0,0 +1,170 @@
1
+ {
2
+ "name": "@pagopa/io-react-native-jwt",
3
+ "version": "0.4.1",
4
+ "description": "Native support for JWT",
5
+ "main": "lib/commonjs/index",
6
+ "module": "lib/module/index",
7
+ "types": "lib/typescript/index.d.ts",
8
+ "react-native": "src/index",
9
+ "source": "src/index",
10
+ "files": [
11
+ "src",
12
+ "lib",
13
+ "android",
14
+ "ios",
15
+ "cpp",
16
+ "*.podspec",
17
+ "!lib/typescript/example",
18
+ "!ios/build",
19
+ "!android/build",
20
+ "!android/gradle",
21
+ "!android/gradlew",
22
+ "!android/gradlew.bat",
23
+ "!android/local.properties",
24
+ "!**/__tests__",
25
+ "!**/__fixtures__",
26
+ "!**/__mocks__",
27
+ "!**/.*"
28
+ ],
29
+ "scripts": {
30
+ "test": "jest",
31
+ "typecheck": "tsc --noEmit",
32
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
33
+ "prepack": "bob build",
34
+ "release": "release-it",
35
+ "example": "yarn --cwd example",
36
+ "build:android": "cd example/android && ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a",
37
+ "build:ios": "cd example/ios && xcodebuild -workspace IoReactNativeJwtExample.xcworkspace -scheme IoReactNativeJwtExample -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO",
38
+ "bootstrap": "yarn example && yarn install && yarn example pods",
39
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build",
40
+ "docs:generate": "typedoc"
41
+ },
42
+ "keywords": [
43
+ "react-native",
44
+ "ios",
45
+ "android"
46
+ ],
47
+ "repository": "https://github.com/pagopa/io-react-native-jwt",
48
+ "author": "PagoPa (https://github.com/pagopa)",
49
+ "license": "MIT",
50
+ "bugs": {
51
+ "url": "https://github.com/pagopa/io-react-native-jwt/issues"
52
+ },
53
+ "homepage": "https://github.com/pagopa/io-react-native-jwt#readme",
54
+ "publishConfig": {
55
+ "registry": "https://registry.npmjs.org/"
56
+ },
57
+ "devDependencies": {
58
+ "@commitlint/config-conventional": "^17.0.2",
59
+ "@evilmartians/lefthook": "^1.2.2",
60
+ "@react-native-community/eslint-config": "^3.0.2",
61
+ "@release-it/conventional-changelog": "^5.0.0",
62
+ "@types/jest": "^28.1.2",
63
+ "@types/react": "~17.0.21",
64
+ "@types/react-native": "0.70.0",
65
+ "commitlint": "^17.0.2",
66
+ "del-cli": "^5.0.0",
67
+ "eslint": "^8.4.1",
68
+ "eslint-config-prettier": "^8.5.0",
69
+ "eslint-plugin-prettier": "^4.0.0",
70
+ "jest": "^28.1.1",
71
+ "pod-install": "^0.1.0",
72
+ "prettier": "^2.0.5",
73
+ "react": "18.2.0",
74
+ "react-native": "0.72.1",
75
+ "react-native-builder-bob": "^0.20.0",
76
+ "release-it": "^15.0.0",
77
+ "turbo": "^1.10.7",
78
+ "typedoc": "^0.24.8",
79
+ "typedoc-plugin-markdown": "^3.15.3",
80
+ "typedoc-plugin-mdn-links": "^3.0.3",
81
+ "typescript": "^5.0.2"
82
+ },
83
+ "resolutions": {
84
+ "@types/react": "17.0.21"
85
+ },
86
+ "peerDependencies": {
87
+ "react": "*",
88
+ "react-native": "*"
89
+ },
90
+ "engines": {
91
+ "node": ">= 16.0.0"
92
+ },
93
+ "packageManager": "^yarn@1.22.15",
94
+ "jest": {
95
+ "preset": "react-native",
96
+ "modulePathIgnorePatterns": [
97
+ "<rootDir>/example/node_modules",
98
+ "<rootDir>/lib/"
99
+ ]
100
+ },
101
+ "commitlint": {
102
+ "extends": [
103
+ "@commitlint/config-conventional"
104
+ ]
105
+ },
106
+ "release-it": {
107
+ "git": {
108
+ "commitMessage": "chore: release ${version}",
109
+ "tagName": "v${version}"
110
+ },
111
+ "npm": {
112
+ "publish": true
113
+ },
114
+ "github": {
115
+ "release": true
116
+ },
117
+ "plugins": {
118
+ "@release-it/conventional-changelog": {
119
+ "preset": "angular"
120
+ }
121
+ }
122
+ },
123
+ "eslintConfig": {
124
+ "root": true,
125
+ "extends": [
126
+ "@react-native-community",
127
+ "prettier"
128
+ ],
129
+ "rules": {
130
+ "prettier/prettier": [
131
+ "error",
132
+ {
133
+ "quoteProps": "consistent",
134
+ "singleQuote": true,
135
+ "tabWidth": 2,
136
+ "trailingComma": "es5",
137
+ "useTabs": false
138
+ }
139
+ ]
140
+ }
141
+ },
142
+ "eslintIgnore": [
143
+ "node_modules/",
144
+ "lib/"
145
+ ],
146
+ "prettier": {
147
+ "quoteProps": "consistent",
148
+ "singleQuote": true,
149
+ "tabWidth": 2,
150
+ "trailingComma": "es5",
151
+ "useTabs": false
152
+ },
153
+ "react-native-builder-bob": {
154
+ "source": "src",
155
+ "output": "lib",
156
+ "targets": [
157
+ "commonjs",
158
+ "module",
159
+ [
160
+ "typescript",
161
+ {
162
+ "project": "tsconfig.build.json"
163
+ }
164
+ ]
165
+ ]
166
+ },
167
+ "dependencies": {
168
+ "abab": "^2.0.6"
169
+ }
170
+ }
@@ -0,0 +1,44 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
5
+
6
+ Pod::Spec.new do |s|
7
+ s.name = "pagopa-io-react-native-jwt"
8
+ s.version = package["version"]
9
+ s.summary = package["description"]
10
+ s.homepage = package["homepage"]
11
+ s.license = package["license"]
12
+ s.authors = package["author"]
13
+
14
+ s.platforms = { :ios => "11.0" }
15
+ s.source = { :git => "https://github.com/pagopa/react-native-jwt.git", :tag => "#{s.version}" }
16
+
17
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
18
+
19
+ #JOSESwift dependency
20
+ s.dependency "JOSESwift", "~> 2.3"
21
+
22
+ # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
23
+ # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
24
+ if respond_to?(:install_modules_dependencies, true)
25
+ install_modules_dependencies(s)
26
+ else
27
+ s.dependency "React-Core"
28
+
29
+ # Don't install the dependencies when we run `pod install` in the old architecture.
30
+ if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
31
+ s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
32
+ s.pod_target_xcconfig = {
33
+ "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
34
+ "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
35
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
36
+ }
37
+ s.dependency "React-Codegen"
38
+ s.dependency "RCT-Folly"
39
+ s.dependency "RCTRequired"
40
+ s.dependency "RCTTypeSafety"
41
+ s.dependency "ReactCommon/turbomodule/core"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,46 @@
1
+ import { JOSENotSupported } from './utils/errors';
2
+
3
+ export const supportedAlgorithms = [
4
+ 'RS256',
5
+ 'RS384',
6
+ 'RS512',
7
+ 'PS256',
8
+ 'PS384',
9
+ 'PS512',
10
+ 'ES256',
11
+ 'ES384',
12
+ 'ES512',
13
+ ];
14
+
15
+ export const isAlgSupported = (alg: string) =>
16
+ supportedAlgorithms.includes(alg.toUpperCase());
17
+
18
+ export const getKtyFromAlg = (alg: string) => {
19
+ switch (alg.slice(0, 2)) {
20
+ case 'RS':
21
+ case 'PS':
22
+ return 'RSA';
23
+ case 'ES':
24
+ return 'EC';
25
+ default:
26
+ throw new JOSENotSupported(
27
+ 'Unsupported "alg" value for a JSON Web Key Set'
28
+ );
29
+ }
30
+ };
31
+
32
+ // Coordinates length in bytes: bits/8
33
+ export const getCoordinateOctetLength = (alg: string) => {
34
+ switch (alg.slice(2, 5)) {
35
+ case '256':
36
+ return 32;
37
+ case '384':
38
+ return 48;
39
+ case '512':
40
+ return 66;
41
+ default:
42
+ throw new JOSENotSupported(
43
+ 'Unsupported "alg" value for a JSON Web Key Set'
44
+ );
45
+ }
46
+ };
package/src/hash.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { removePadding } from './utils/base64';
2
+ import { IoReactNativeJwt } from './utils/proxy';
3
+
4
+ /**
5
+ * Return base64 encoded SHA256 hash of input string
6
+ */
7
+ export const sha256ToBase64 = async (toHash: string): Promise<string> => {
8
+ let hash = await IoReactNativeJwt.sha256(toHash);
9
+ return removePadding(hash);
10
+ };
package/src/index.tsx ADDED
@@ -0,0 +1,120 @@
1
+ import type {
2
+ JWK,
3
+ JWTClaimVerificationOptions,
4
+ JWTDecodeResult,
5
+ } from './types';
6
+ import jwtPayload from './jwt_claims_set';
7
+ import { JWSSignatureVerificationFailed } from './utils/errors';
8
+ import { IoReactNativeJwt } from './utils/proxy';
9
+ import { SignJWT } from './sign';
10
+
11
+ export * from './produce';
12
+ export * from './sign';
13
+ export * from './unsecured';
14
+ export { derToJose } from './utils/asn1';
15
+ export { encodeBase64, decodeBase64 } from './utils/base64';
16
+ export { sha256ToBase64 } from './hash';
17
+ /**
18
+ * Decode the JWT without validation.
19
+ *
20
+ * @example Usage with a public JWK encoded RSA key
21
+ *
22
+ * ```js
23
+ * const jwt =
24
+ * 'eyJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCIsImtpZCI6IkVDIzEiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJodHRwczovL2lvLWQtd2FsbGV0LWl0LmF6dXJld2Vic2l0ZXMubmV0LyIsInN1YiI6Imh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvIiwibWV0YWRhdGEiOnsiZXVkaV93YWxsZXRfcHJvdmlkZXIiOnsiandrcyI6W3siY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiJxckpyajNBZl9CNTdzYk9JUnJjQk03YnI3d09jOHluajdsSEZQVGVmZlVrIiwieSI6IjFIMGNXRHlHZ3ZVOHcta1BLVV94eWNPQ1VOVDJvMGJ3c2xJUXRuUFU2aU0iLCJraWQiOiJFQyMxIn1dLCJ0b2tlbl9lbmRwb2ludCI6Imh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvdG9rZW4iLCJhc2NfdmFsdWVzX3N1cHBvcnRlZCI6WyJodHRwczovL2lvLWQtd2FsbGV0LWl0LmF6dXJld2Vic2l0ZXMubmV0L0xvQS9iYXNpYyIsImh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvTG9BL21lZGl1bSIsImh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvTG9BL2hpZ2h0Il0sImdyYW50X3R5cGVzX3N1cHBvcnRlZCI6WyJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Y2xpZW50LWFzc2VydGlvbi10eXBlOmp3dC1rZXktYXR0ZXN0YXRpb24iXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2RzX3N1cHBvcnRlZCI6WyJwcml2YXRlX2tleV9qd3QiXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVTMjU2IiwiRVMyNTZLIiwiRVMzODQiLCJFUzUxMiIsIlJTMjU2IiwiUlMzODQiLCJSUzUxMiIsIlBTMjU2IiwiUFMzODQiLCJQUzUxMiJdfSwiZmVkZXJhdGlvbl9lbnRpdHkiOnsib3JnYW5pemF0aW9uX25hbWUiOiJQYWdvUGEgUy5wLkEuIiwiaG9tZXBhZ2VfdXJpIjoiaHR0cHM6Ly9pby5pdGFsaWEuaXQvIiwicG9saWN5X3VyaSI6Imh0dHBzOi8vaW8uaXRhbGlhLml0L3ByaXZhY3ktcG9saWN5LyIsInRvc191cmkiOiJodHRwczovL2lvLml0YWxpYS5pdC9wcml2YWN5LXBvbGljeS8iLCJsb2dvX3VyaSI6Imh0dHBzOi8vaW8uaXRhbGlhLml0L2Fzc2V0cy9pbWcvaW8taXQtbG9nby13aGl0ZS5zdmcifX0sImlhdCI6MTY4OTIzNjYzNSwiZXhwIjoxNjg5MjQwMjM1fQ.6wA0M6rNYNSFN_EylzMA6ElAibW7FVSZyoLNEkHU5c_RKuiNenT08YIMvbysYautLZotUedEMP5xCyNpY34x6Q'
25
+ *
26
+ * const { payload, protectedHeader } = await decode(jwt)
27
+ *
28
+ * ```
29
+ *
30
+ * @param jwt JSON Web Token value (encoded as JWS).
31
+ */
32
+ export const decode = (token: string): JWTDecodeResult => SignJWT.decode(token);
33
+
34
+ /**
35
+ * Verifies the JWS signature
36
+ *
37
+ * @example Usage with a public JWK encoded RSA key
38
+ *
39
+ * ```js
40
+ * const jwk = {
41
+ * crv: 'P-256',
42
+ * kty: 'EC',
43
+ * x: 'qrJrj3Af_B57sbOIRrcBM7br7wOc8ynj7lHFPTeffUk',
44
+ * y: '1H0cWDyGgvU8w-kPKU_xycOCUNT2o0bwslIQtnPU6iM',
45
+ * }
46
+ * const jwt =
47
+ * 'eyJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCIsImtpZCI6IkVDIzEiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJodHRwczovL2lvLWQtd2FsbGV0LWl0LmF6dXJld2Vic2l0ZXMubmV0LyIsInN1YiI6Imh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvIiwibWV0YWRhdGEiOnsiZXVkaV93YWxsZXRfcHJvdmlkZXIiOnsiandrcyI6W3siY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiJxckpyajNBZl9CNTdzYk9JUnJjQk03YnI3d09jOHluajdsSEZQVGVmZlVrIiwieSI6IjFIMGNXRHlHZ3ZVOHcta1BLVV94eWNPQ1VOVDJvMGJ3c2xJUXRuUFU2aU0iLCJraWQiOiJFQyMxIn1dLCJ0b2tlbl9lbmRwb2ludCI6Imh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvdG9rZW4iLCJhc2NfdmFsdWVzX3N1cHBvcnRlZCI6WyJodHRwczovL2lvLWQtd2FsbGV0LWl0LmF6dXJld2Vic2l0ZXMubmV0L0xvQS9iYXNpYyIsImh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvTG9BL21lZGl1bSIsImh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvTG9BL2hpZ2h0Il0sImdyYW50X3R5cGVzX3N1cHBvcnRlZCI6WyJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Y2xpZW50LWFzc2VydGlvbi10eXBlOmp3dC1rZXktYXR0ZXN0YXRpb24iXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2RzX3N1cHBvcnRlZCI6WyJwcml2YXRlX2tleV9qd3QiXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVTMjU2IiwiRVMyNTZLIiwiRVMzODQiLCJFUzUxMiIsIlJTMjU2IiwiUlMzODQiLCJSUzUxMiIsIlBTMjU2IiwiUFMzODQiLCJQUzUxMiJdfSwiZmVkZXJhdGlvbl9lbnRpdHkiOnsib3JnYW5pemF0aW9uX25hbWUiOiJQYWdvUGEgUy5wLkEuIiwiaG9tZXBhZ2VfdXJpIjoiaHR0cHM6Ly9pby5pdGFsaWEuaXQvIiwicG9saWN5X3VyaSI6Imh0dHBzOi8vaW8uaXRhbGlhLml0L3ByaXZhY3ktcG9saWN5LyIsInRvc191cmkiOiJodHRwczovL2lvLml0YWxpYS5pdC9wcml2YWN5LXBvbGljeS8iLCJsb2dvX3VyaSI6Imh0dHBzOi8vaW8uaXRhbGlhLml0L2Fzc2V0cy9pbWcvaW8taXQtbG9nby13aGl0ZS5zdmcifX0sImlhdCI6MTY4OTIzNjYzNSwiZXhwIjoxNjg5MjQwMjM1fQ.6wA0M6rNYNSFN_EylzMA6ElAibW7FVSZyoLNEkHU5c_RKuiNenT08YIMvbysYautLZotUedEMP5xCyNpY34x6Q'
48
+ *
49
+ * const isSignatureValid = await verify(jwt, jwk)
50
+ * ```
51
+ *
52
+ * @param jwt JSON Web Token value (encoded as JWS).
53
+ * @param key Public key to verify the JWT in JWK format.
54
+ */
55
+ export const isSignatureValid = (token: string, jwk: JWK): Promise<boolean> =>
56
+ IoReactNativeJwt.verify(token, jwk);
57
+
58
+ /**
59
+ * Verifies the JWT format (to be a JWS Compact format), verifies the JWS signature, validates the
60
+ * JWT Claims Set.
61
+ *
62
+ * @example Usage with a public JWK encoded RSA key
63
+ *
64
+ * ```js
65
+ * const jwk = {
66
+ * crv: 'P-256',
67
+ * kty: 'EC',
68
+ * x: 'qrJrj3Af_B57sbOIRrcBM7br7wOc8ynj7lHFPTeffUk',
69
+ * y: '1H0cWDyGgvU8w-kPKU_xycOCUNT2o0bwslIQtnPU6iM',
70
+ * }
71
+ * const jwt =
72
+ * 'eyJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCIsImtpZCI6IkVDIzEiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJodHRwczovL2lvLWQtd2FsbGV0LWl0LmF6dXJld2Vic2l0ZXMubmV0LyIsInN1YiI6Imh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvIiwibWV0YWRhdGEiOnsiZXVkaV93YWxsZXRfcHJvdmlkZXIiOnsiandrcyI6W3siY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiJxckpyajNBZl9CNTdzYk9JUnJjQk03YnI3d09jOHluajdsSEZQVGVmZlVrIiwieSI6IjFIMGNXRHlHZ3ZVOHcta1BLVV94eWNPQ1VOVDJvMGJ3c2xJUXRuUFU2aU0iLCJraWQiOiJFQyMxIn1dLCJ0b2tlbl9lbmRwb2ludCI6Imh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvdG9rZW4iLCJhc2NfdmFsdWVzX3N1cHBvcnRlZCI6WyJodHRwczovL2lvLWQtd2FsbGV0LWl0LmF6dXJld2Vic2l0ZXMubmV0L0xvQS9iYXNpYyIsImh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvTG9BL21lZGl1bSIsImh0dHBzOi8vaW8tZC13YWxsZXQtaXQuYXp1cmV3ZWJzaXRlcy5uZXQvTG9BL2hpZ2h0Il0sImdyYW50X3R5cGVzX3N1cHBvcnRlZCI6WyJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Y2xpZW50LWFzc2VydGlvbi10eXBlOmp3dC1rZXktYXR0ZXN0YXRpb24iXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2RzX3N1cHBvcnRlZCI6WyJwcml2YXRlX2tleV9qd3QiXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVTMjU2IiwiRVMyNTZLIiwiRVMzODQiLCJFUzUxMiIsIlJTMjU2IiwiUlMzODQiLCJSUzUxMiIsIlBTMjU2IiwiUFMzODQiLCJQUzUxMiJdfSwiZmVkZXJhdGlvbl9lbnRpdHkiOnsib3JnYW5pemF0aW9uX25hbWUiOiJQYWdvUGEgUy5wLkEuIiwiaG9tZXBhZ2VfdXJpIjoiaHR0cHM6Ly9pby5pdGFsaWEuaXQvIiwicG9saWN5X3VyaSI6Imh0dHBzOi8vaW8uaXRhbGlhLml0L3ByaXZhY3ktcG9saWN5LyIsInRvc191cmkiOiJodHRwczovL2lvLml0YWxpYS5pdC9wcml2YWN5LXBvbGljeS8iLCJsb2dvX3VyaSI6Imh0dHBzOi8vaW8uaXRhbGlhLml0L2Fzc2V0cy9pbWcvaW8taXQtbG9nby13aGl0ZS5zdmcifX0sImlhdCI6MTY4OTIzNjYzNSwiZXhwIjoxNjg5MjQwMjM1fQ.6wA0M6rNYNSFN_EylzMA6ElAibW7FVSZyoLNEkHU5c_RKuiNenT08YIMvbysYautLZotUedEMP5xCyNpY34x6Q'
73
+ *
74
+ * const { payload, protectedHeader } = await verify(jwt, jwk, {
75
+ * typ: 'entity-statement+jwt',
76
+ * requiredClaims: ['iss', 'sub', 'metadata'],
77
+ * })
78
+ * ```
79
+ *
80
+ * @param jwt JSON Web Token value (encoded as JWS).
81
+ * @param key Public key to verify the JWT in JWK format.
82
+ * @param options JWT Decryption and JWT Claims Set validation options.
83
+ */
84
+ export const verify = async (
85
+ token: string,
86
+ jwk: JWK,
87
+ options: JWTClaimVerificationOptions = {}
88
+ ): Promise<JWTDecodeResult> => {
89
+ const { protectedHeader, payload } = await decode(token);
90
+ const signatureIsValid = await isSignatureValid(token, jwk);
91
+
92
+ if (signatureIsValid) {
93
+ const verifiedPayload = jwtPayload(protectedHeader, payload, options);
94
+ return { payload: verifiedPayload, protectedHeader };
95
+ } else {
96
+ throw new JWSSignatureVerificationFailed();
97
+ }
98
+ };
99
+
100
+ /**
101
+ * Calculate JWK thumbprint
102
+ *
103
+ * @example Get JWK thumbprint
104
+ *
105
+ * ```js
106
+ * const jwk: JWK = {
107
+ * crv: 'P-256',
108
+ * kty: 'EC',
109
+ * x: 'qrJrj3Af_B57sbOIRrcBM7br7wOc8ynj7lHFPTeffUk',
110
+ * y: '1H0cWDyGgvU8w-kPKU_xycOCUNT2o0bwslIQtnPU6iM',
111
+ * };
112
+
113
+ * const thumbprint = await thumbprint(jwk)
114
+ *
115
+ * ```
116
+ *
117
+ * @param jwk JSON Web Key
118
+ */
119
+ export const thumbprint = (jwk: JWK): Promise<string> =>
120
+ IoReactNativeJwt.thumbprint(jwk);
@@ -0,0 +1,194 @@
1
+ /*
2
+ * This code comes from https://github.com/panva/jose
3
+ */
4
+ import type {
5
+ JWTPayload,
6
+ JWTClaimVerificationOptions,
7
+ JWSHeaderParameters,
8
+ } from './types';
9
+ import { JWTClaimValidationFailed, JWTExpired } from './utils/errors';
10
+
11
+ import secs, { epoch } from './utils/secs';
12
+
13
+ const normalizeTyp = (value: string) =>
14
+ value.toLowerCase().replace(/^application\//, '');
15
+
16
+ const checkAudiencePresence = (audPayload: unknown, audOption: unknown[]) => {
17
+ if (typeof audPayload === 'string') {
18
+ return audOption.includes(audPayload);
19
+ }
20
+
21
+ if (Array.isArray(audPayload)) {
22
+ /*
23
+ * Each principal intended to process the JWT MUST
24
+ * identify itself with a value in the audience claim
25
+ */
26
+ return audOption.some(Set.prototype.has.bind(new Set(audPayload)));
27
+ }
28
+
29
+ return false;
30
+ };
31
+
32
+ export default (
33
+ protectedHeader: JWSHeaderParameters,
34
+ payload: JWTPayload,
35
+ options: JWTClaimVerificationOptions = {}
36
+ ) => {
37
+ const { typ } = options;
38
+ if (
39
+ typ &&
40
+ (typeof protectedHeader!.typ !== 'string' ||
41
+ normalizeTyp(protectedHeader!.typ) !== normalizeTyp(typ))
42
+ ) {
43
+ throw new JWTClaimValidationFailed(
44
+ 'unexpected "typ" JWT header value',
45
+ 'typ',
46
+ 'check_failed'
47
+ );
48
+ }
49
+
50
+ const {
51
+ requiredClaims = [],
52
+ issuer,
53
+ subject,
54
+ audience,
55
+ maxTokenAge,
56
+ } = options;
57
+
58
+ if (maxTokenAge !== undefined) requiredClaims.push('iat');
59
+ if (audience !== undefined) requiredClaims.push('aud');
60
+ if (subject !== undefined) requiredClaims.push('sub');
61
+ if (issuer !== undefined) requiredClaims.push('iss');
62
+
63
+ for (const claim of new Set(requiredClaims.reverse())) {
64
+ if (!(claim in payload)) {
65
+ throw new JWTClaimValidationFailed(
66
+ `missing required "${claim}" claim`,
67
+ claim,
68
+ 'missing'
69
+ );
70
+ }
71
+ }
72
+
73
+ if (
74
+ issuer &&
75
+ !(<unknown[]>(Array.isArray(issuer) ? issuer : [issuer])).includes(
76
+ payload.iss!
77
+ )
78
+ ) {
79
+ throw new JWTClaimValidationFailed(
80
+ 'unexpected "iss" claim value',
81
+ 'iss',
82
+ 'check_failed'
83
+ );
84
+ }
85
+
86
+ if (subject && payload.sub !== subject) {
87
+ throw new JWTClaimValidationFailed(
88
+ 'unexpected "sub" claim value',
89
+ 'sub',
90
+ 'check_failed'
91
+ );
92
+ }
93
+
94
+ if (
95
+ audience &&
96
+ !checkAudiencePresence(
97
+ payload.aud,
98
+ typeof audience === 'string' ? [audience] : audience
99
+ )
100
+ ) {
101
+ throw new JWTClaimValidationFailed(
102
+ 'unexpected "aud" claim value',
103
+ 'aud',
104
+ 'check_failed'
105
+ );
106
+ }
107
+
108
+ let tolerance: number;
109
+ switch (typeof options.clockTolerance) {
110
+ case 'string':
111
+ tolerance = secs(options.clockTolerance);
112
+ break;
113
+ case 'number':
114
+ tolerance = options.clockTolerance;
115
+ break;
116
+ case 'undefined':
117
+ tolerance = 0;
118
+ break;
119
+ default:
120
+ throw new TypeError('Invalid clockTolerance option type');
121
+ }
122
+
123
+ const { currentDate } = options;
124
+ const now = epoch(currentDate || new Date());
125
+
126
+ if (
127
+ (payload.iat !== undefined || maxTokenAge) &&
128
+ typeof payload.iat !== 'number'
129
+ ) {
130
+ throw new JWTClaimValidationFailed(
131
+ '"iat" claim must be a number',
132
+ 'iat',
133
+ 'invalid'
134
+ );
135
+ }
136
+
137
+ if (payload.nbf !== undefined) {
138
+ if (typeof payload.nbf !== 'number') {
139
+ throw new JWTClaimValidationFailed(
140
+ '"nbf" claim must be a number',
141
+ 'nbf',
142
+ 'invalid'
143
+ );
144
+ }
145
+ if (payload.nbf > now + tolerance) {
146
+ throw new JWTClaimValidationFailed(
147
+ '"nbf" claim timestamp check failed',
148
+ 'nbf',
149
+ 'check_failed'
150
+ );
151
+ }
152
+ }
153
+
154
+ if (payload.exp !== undefined) {
155
+ if (typeof payload.exp !== 'number') {
156
+ throw new JWTClaimValidationFailed(
157
+ '"exp" claim must be a number',
158
+ 'exp',
159
+ 'invalid'
160
+ );
161
+ }
162
+ if (payload.exp <= now - tolerance) {
163
+ throw new JWTExpired(
164
+ '"exp" claim timestamp check failed',
165
+ 'exp',
166
+ 'check_failed'
167
+ );
168
+ }
169
+ }
170
+
171
+ if (maxTokenAge) {
172
+ const age = now - payload.iat!;
173
+ const max =
174
+ typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);
175
+
176
+ if (age - tolerance > max) {
177
+ throw new JWTExpired(
178
+ '"iat" claim timestamp check failed (too far in the past)',
179
+ 'iat',
180
+ 'check_failed'
181
+ );
182
+ }
183
+
184
+ if (age < 0 - tolerance) {
185
+ throw new JWTClaimValidationFailed(
186
+ '"iat" claim timestamp check failed (it should be in the past)',
187
+ 'iat',
188
+ 'check_failed'
189
+ );
190
+ }
191
+ }
192
+
193
+ return <JWTPayload>payload;
194
+ };
package/src/produce.ts ADDED
@@ -0,0 +1,109 @@
1
+ import type { JWTPayload } from './types';
2
+ import isObject from './utils/is_object';
3
+ import secs, { epoch } from './utils/secs';
4
+
5
+ /** Generic class for JWT producing. */
6
+ export class ProduceJWT {
7
+ protected _payload!: JWTPayload;
8
+
9
+ /** @param payload The JWT Claims Set object. */
10
+ constructor(payload: JWTPayload) {
11
+ if (!isObject(payload)) {
12
+ throw new TypeError('JWT Claims Set MUST be an object');
13
+ }
14
+ this._payload = payload;
15
+ }
16
+
17
+ /**
18
+ * Set "iss" (Issuer) Claim.
19
+ *
20
+ * @param issuer "Issuer" Claim value to set on the JWT Claims Set.
21
+ */
22
+ setIssuer(issuer: string) {
23
+ this._payload = { ...this._payload, iss: issuer };
24
+ return this;
25
+ }
26
+
27
+ /**
28
+ * Set "sub" (Subject) Claim.
29
+ *
30
+ * @param subject "sub" (Subject) Claim value to set on the JWT Claims Set.
31
+ */
32
+ setSubject(subject: string) {
33
+ this._payload = { ...this._payload, sub: subject };
34
+ return this;
35
+ }
36
+
37
+ /**
38
+ * Set "aud" (Audience) Claim.
39
+ *
40
+ * @param audience "aud" (Audience) Claim value to set on the JWT Claims Set.
41
+ */
42
+ setAudience(audience: string | string[]) {
43
+ this._payload = { ...this._payload, aud: audience };
44
+ return this;
45
+ }
46
+
47
+ /**
48
+ * Set "jti" (JWT ID) Claim.
49
+ *
50
+ * @param jwtId "jti" (JWT ID) Claim value to set on the JWT Claims Set.
51
+ */
52
+ setJti(jwtId: string) {
53
+ this._payload = { ...this._payload, jti: jwtId };
54
+ return this;
55
+ }
56
+
57
+ /**
58
+ * Set "nbf" (Not Before) Claim.
59
+ *
60
+ * @param input "nbf" (Not Before) Claim value to set on the JWT Claims Set. When number is passed
61
+ * that is used as a value, when string is passed it is resolved to a time span and added to the
62
+ * current timestamp.
63
+ */
64
+ setNotBefore(input: number | string) {
65
+ if (typeof input === 'number') {
66
+ this._payload = { ...this._payload, nbf: input };
67
+ } else {
68
+ this._payload = {
69
+ ...this._payload,
70
+ nbf: epoch(new Date()) + secs(input),
71
+ };
72
+ }
73
+ return this;
74
+ }
75
+
76
+ /**
77
+ * Set "exp" (Expiration Time) Claim.
78
+ *
79
+ * @param input "exp" (Expiration Time) Claim value to set on the JWT Claims Set. When number is
80
+ * passed that is used as a value, when string is passed it is resolved to a time span and added
81
+ * to the current timestamp.
82
+ */
83
+ setExpirationTime(input: number | string) {
84
+ if (typeof input === 'number') {
85
+ this._payload = { ...this._payload, exp: input };
86
+ } else {
87
+ this._payload = {
88
+ ...this._payload,
89
+ exp: epoch(new Date()) + secs(input),
90
+ };
91
+ }
92
+ return this;
93
+ }
94
+
95
+ /**
96
+ * Set "iat" (Issued At) Claim.
97
+ *
98
+ * @param input "iat" (Issued At) Claim value to set on the JWT Claims Set. Default is current
99
+ * timestamp.
100
+ */
101
+ setIssuedAt(input?: number) {
102
+ if (typeof input === 'undefined') {
103
+ this._payload = { ...this._payload, iat: epoch(new Date()) };
104
+ } else {
105
+ this._payload = { ...this._payload, iat: input };
106
+ }
107
+ return this;
108
+ }
109
+ }