noresponse 2.0.17

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 (231) hide show
  1. checksums.yaml +7 -0
  2. data/lib/crystalline/enum.rbi +17 -0
  3. data/lib/crystalline/metadata_fields.rb +263 -0
  4. data/lib/crystalline/module.rb +186 -0
  5. data/lib/crystalline/types.rb +168 -0
  6. data/lib/crystalline/utils.rb +67 -0
  7. data/lib/crystalline.rb +11 -0
  8. data/lib/no_response/assessments.rb +1252 -0
  9. data/lib/no_response/auth.rb +263 -0
  10. data/lib/no_response/issued_credential.rb +21 -0
  11. data/lib/no_response/keys.rb +318 -0
  12. data/lib/no_response/late_results.rb +301 -0
  13. data/lib/no_response/models/callbacks.rb +10 -0
  14. data/lib/no_response/models/errors/apistatuserror.rb +41 -0
  15. data/lib/no_response/models/errors/apistatuserror.rbi +30 -0
  16. data/lib/no_response/models/errors/authenticationerror.rb +108 -0
  17. data/lib/no_response/models/errors/authenticationerror.rbi +56 -0
  18. data/lib/no_response/models/errors/badrequesterror.rb +108 -0
  19. data/lib/no_response/models/errors/badrequesterror.rbi +56 -0
  20. data/lib/no_response/models/errors/conflicterror.rb +108 -0
  21. data/lib/no_response/models/errors/conflicterror.rbi +56 -0
  22. data/lib/no_response/models/errors/notfounderror.rb +108 -0
  23. data/lib/no_response/models/errors/notfounderror.rbi +56 -0
  24. data/lib/no_response/models/errors/permissiondeniederror.rb +108 -0
  25. data/lib/no_response/models/errors/permissiondeniederror.rbi +56 -0
  26. data/lib/no_response/models/errors/ratelimiterror.rb +108 -0
  27. data/lib/no_response/models/errors/ratelimiterror.rbi +56 -0
  28. data/lib/no_response/models/errors.rb +16 -0
  29. data/lib/no_response/models/operations/createassessment_request.rb +36 -0
  30. data/lib/no_response/models/operations/createassessment_request.rbi +20 -0
  31. data/lib/no_response/models/operations/createassessment_response.rb +32 -0
  32. data/lib/no_response/models/operations/createassessment_response.rbi +20 -0
  33. data/lib/no_response/models/operations/getassessment_request.rb +41 -0
  34. data/lib/no_response/models/operations/getassessment_request.rbi +20 -0
  35. data/lib/no_response/models/operations/getassessment_response.rb +32 -0
  36. data/lib/no_response/models/operations/getassessment_response.rbi +20 -0
  37. data/lib/no_response/models/operations/queryaccountassessments_response.rb +44 -0
  38. data/lib/no_response/models/operations/queryaccountassessments_response.rbi +20 -0
  39. data/lib/no_response/models/operations/recordassessmentoutcome_request.rb +52 -0
  40. data/lib/no_response/models/operations/recordassessmentoutcome_request.rbi +26 -0
  41. data/lib/no_response/models/operations/recordassessmentoutcome_response.rb +32 -0
  42. data/lib/no_response/models/operations/recordassessmentoutcome_response.rbi +20 -0
  43. data/lib/no_response/models/operations/rotateapikey_response.rb +32 -0
  44. data/lib/no_response/models/operations/rotateapikey_response.rbi +20 -0
  45. data/lib/no_response/models/operations.rb +17 -0
  46. data/lib/no_response/models/shared/alternate_name.rb +23 -0
  47. data/lib/no_response/models/shared/alternate_name.rbi +9 -0
  48. data/lib/no_response/models/shared/alternate_name_union.rb +23 -0
  49. data/lib/no_response/models/shared/alternate_name_union.rbi +9 -0
  50. data/lib/no_response/models/shared/assessment.rb +235 -0
  51. data/lib/no_response/models/shared/assessment.rbi +116 -0
  52. data/lib/no_response/models/shared/assessmentlist.rb +78 -0
  53. data/lib/no_response/models/shared/assessmentlist.rbi +38 -0
  54. data/lib/no_response/models/shared/assessmentoutcome.rb +61 -0
  55. data/lib/no_response/models/shared/assessmentoutcome.rbi +32 -0
  56. data/lib/no_response/models/shared/assessmentoutcomerequest.rb +71 -0
  57. data/lib/no_response/models/shared/assessmentoutcomerequest.rbi +32 -0
  58. data/lib/no_response/models/shared/assessmentquery.rb +70 -0
  59. data/lib/no_response/models/shared/assessmentquery.rbi +32 -0
  60. data/lib/no_response/models/shared/assessmentrequest.rb +125 -0
  61. data/lib/no_response/models/shared/assessmentrequest.rbi +62 -0
  62. data/lib/no_response/models/shared/assessmentrequestclientcontext.rb +125 -0
  63. data/lib/no_response/models/shared/assessmentrequestclientcontext.rbi +62 -0
  64. data/lib/no_response/models/shared/assessmentrequestidentifiers.rb +74 -0
  65. data/lib/no_response/models/shared/assessmentrequestidentifiers.rbi +38 -0
  66. data/lib/no_response/models/shared/assessmentrequestissuedcredential.rb +33 -0
  67. data/lib/no_response/models/shared/assessmentrequestissuedcredential.rbi +14 -0
  68. data/lib/no_response/models/shared/assessmentrequestpolicycontext.rb +46 -0
  69. data/lib/no_response/models/shared/assessmentrequestpolicycontext.rbi +20 -0
  70. data/lib/no_response/models/shared/assessmenttiming.rb +41 -0
  71. data/lib/no_response/models/shared/assessmenttiming.rbi +20 -0
  72. data/lib/no_response/models/shared/assurancelevel.rb +18 -0
  73. data/lib/no_response/models/shared/assurancelevel.rbi +9 -0
  74. data/lib/no_response/models/shared/authcontext.rb +285 -0
  75. data/lib/no_response/models/shared/authcontext.rbi +152 -0
  76. data/lib/no_response/models/shared/authcontextmfa.rb +55 -0
  77. data/lib/no_response/models/shared/authcontextmfa.rbi +26 -0
  78. data/lib/no_response/models/shared/authenticationerrorcode.rb +18 -0
  79. data/lib/no_response/models/shared/authenticationerrorcode.rbi +9 -0
  80. data/lib/no_response/models/shared/badrequesterrorcode.rb +20 -0
  81. data/lib/no_response/models/shared/badrequesterrorcode.rbi +9 -0
  82. data/lib/no_response/models/shared/band.rb +21 -0
  83. data/lib/no_response/models/shared/band.rbi +9 -0
  84. data/lib/no_response/models/shared/breach.rb +108 -0
  85. data/lib/no_response/models/shared/breach.rbi +56 -0
  86. data/lib/no_response/models/shared/breachexposed.rb +28 -0
  87. data/lib/no_response/models/shared/breachexposed.rbi +9 -0
  88. data/lib/no_response/models/shared/breachkind.rb +24 -0
  89. data/lib/no_response/models/shared/breachkind.rbi +9 -0
  90. data/lib/no_response/models/shared/breachselector.rb +20 -0
  91. data/lib/no_response/models/shared/breachselector.rbi +9 -0
  92. data/lib/no_response/models/shared/breachsummary.rb +109 -0
  93. data/lib/no_response/models/shared/breachsummary.rbi +56 -0
  94. data/lib/no_response/models/shared/confidence.rb +21 -0
  95. data/lib/no_response/models/shared/confidence.rbi +9 -0
  96. data/lib/no_response/models/shared/conflicterrorcode.rb +19 -0
  97. data/lib/no_response/models/shared/conflicterrorcode.rbi +9 -0
  98. data/lib/no_response/models/shared/consistency.rb +85 -0
  99. data/lib/no_response/models/shared/consistency.rbi +32 -0
  100. data/lib/no_response/models/shared/consistencyvalue.rb +21 -0
  101. data/lib/no_response/models/shared/consistencyvalue.rbi +9 -0
  102. data/lib/no_response/models/shared/country.rb +20 -0
  103. data/lib/no_response/models/shared/country.rbi +9 -0
  104. data/lib/no_response/models/shared/email.rb +23 -0
  105. data/lib/no_response/models/shared/email.rbi +9 -0
  106. data/lib/no_response/models/shared/email_union.rb +23 -0
  107. data/lib/no_response/models/shared/email_union.rbi +9 -0
  108. data/lib/no_response/models/shared/failurereason.rb +24 -0
  109. data/lib/no_response/models/shared/failurereason.rbi +9 -0
  110. data/lib/no_response/models/shared/flow.rb +18 -0
  111. data/lib/no_response/models/shared/flow.rbi +9 -0
  112. data/lib/no_response/models/shared/freshness.rb +20 -0
  113. data/lib/no_response/models/shared/freshness.rbi +9 -0
  114. data/lib/no_response/models/shared/hookpoint.rb +19 -0
  115. data/lib/no_response/models/shared/hookpoint.rbi +9 -0
  116. data/lib/no_response/models/shared/identifierkind.rb +21 -0
  117. data/lib/no_response/models/shared/identifierkind.rbi +9 -0
  118. data/lib/no_response/models/shared/incompletereason.rb +23 -0
  119. data/lib/no_response/models/shared/incompletereason.rbi +9 -0
  120. data/lib/no_response/models/shared/incompletesource.rb +53 -0
  121. data/lib/no_response/models/shared/incompletesource.rbi +20 -0
  122. data/lib/no_response/models/shared/ipsummary.rb +61 -0
  123. data/lib/no_response/models/shared/ipsummary.rbi +26 -0
  124. data/lib/no_response/models/shared/ipsummarytype.rb +24 -0
  125. data/lib/no_response/models/shared/ipsummarytype.rbi +9 -0
  126. data/lib/no_response/models/shared/keyrotation.rb +79 -0
  127. data/lib/no_response/models/shared/keyrotation.rbi +44 -0
  128. data/lib/no_response/models/shared/keyrotationrequest.rb +33 -0
  129. data/lib/no_response/models/shared/keyrotationrequest.rbi +14 -0
  130. data/lib/no_response/models/shared/lane.rb +21 -0
  131. data/lib/no_response/models/shared/lane.rbi +9 -0
  132. data/lib/no_response/models/shared/linetype.rb +22 -0
  133. data/lib/no_response/models/shared/linetype.rbi +9 -0
  134. data/lib/no_response/models/shared/location.rb +23 -0
  135. data/lib/no_response/models/shared/location.rbi +9 -0
  136. data/lib/no_response/models/shared/location_union.rb +23 -0
  137. data/lib/no_response/models/shared/location_union.rbi +9 -0
  138. data/lib/no_response/models/shared/name_candidate.rb +23 -0
  139. data/lib/no_response/models/shared/name_candidate.rbi +9 -0
  140. data/lib/no_response/models/shared/name_candidate_union.rb +23 -0
  141. data/lib/no_response/models/shared/name_candidate_union.rbi +9 -0
  142. data/lib/no_response/models/shared/notfounderrorcode.rb +18 -0
  143. data/lib/no_response/models/shared/notfounderrorcode.rbi +9 -0
  144. data/lib/no_response/models/shared/outcome.rb +23 -0
  145. data/lib/no_response/models/shared/outcome.rbi +9 -0
  146. data/lib/no_response/models/shared/permissiondeniederrorcode.rb +18 -0
  147. data/lib/no_response/models/shared/permissiondeniederrorcode.rbi +9 -0
  148. data/lib/no_response/models/shared/phoneenrichment.rb +32 -0
  149. data/lib/no_response/models/shared/phoneenrichment.rbi +14 -0
  150. data/lib/no_response/models/shared/phoneenrichmentfields.rb +451 -0
  151. data/lib/no_response/models/shared/phoneenrichmentfields.rbi +212 -0
  152. data/lib/no_response/models/shared/platform.rb +18 -0
  153. data/lib/no_response/models/shared/platform.rbi +9 -0
  154. data/lib/no_response/models/shared/probleminvalidparam.rb +48 -0
  155. data/lib/no_response/models/shared/probleminvalidparam.rbi +26 -0
  156. data/lib/no_response/models/shared/ratelimiterrorcode.rb +18 -0
  157. data/lib/no_response/models/shared/ratelimiterrorcode.rbi +9 -0
  158. data/lib/no_response/models/shared/reasoncode.rb +52 -0
  159. data/lib/no_response/models/shared/reasoncode.rbi +9 -0
  160. data/lib/no_response/models/shared/reasonidentifier.rb +23 -0
  161. data/lib/no_response/models/shared/reasonidentifier.rbi +9 -0
  162. data/lib/no_response/models/shared/recommendation.rb +20 -0
  163. data/lib/no_response/models/shared/recommendation.rbi +9 -0
  164. data/lib/no_response/models/shared/responsemode.rb +16 -0
  165. data/lib/no_response/models/shared/responsemode.rbi +9 -0
  166. data/lib/no_response/models/shared/risk.rb +171 -0
  167. data/lib/no_response/models/shared/risk.rbi +80 -0
  168. data/lib/no_response/models/shared/riskconfidence.rb +20 -0
  169. data/lib/no_response/models/shared/riskconfidence.rbi +9 -0
  170. data/lib/no_response/models/shared/riskcoverage.rb +49 -0
  171. data/lib/no_response/models/shared/riskcoverage.rbi +26 -0
  172. data/lib/no_response/models/shared/riskdetails.rb +55 -0
  173. data/lib/no_response/models/shared/riskdetails.rbi +26 -0
  174. data/lib/no_response/models/shared/riskdetailsbreachname.rb +40 -0
  175. data/lib/no_response/models/shared/riskdetailsbreachname.rbi +20 -0
  176. data/lib/no_response/models/shared/riskdetailsphonename.rb +40 -0
  177. data/lib/no_response/models/shared/riskdetailsphonename.rbi +20 -0
  178. data/lib/no_response/models/shared/riskreason.rb +67 -0
  179. data/lib/no_response/models/shared/riskreason.rbi +26 -0
  180. data/lib/no_response/models/shared/risksummary.rb +73 -0
  181. data/lib/no_response/models/shared/risksummary.rbi +38 -0
  182. data/lib/no_response/models/shared/risksummaryemail.rb +55 -0
  183. data/lib/no_response/models/shared/risksummaryemail.rbi +26 -0
  184. data/lib/no_response/models/shared/risksummaryphone.rb +69 -0
  185. data/lib/no_response/models/shared/risksummaryphone.rbi +32 -0
  186. data/lib/no_response/models/shared/risksummaryusername.rb +41 -0
  187. data/lib/no_response/models/shared/risksummaryusername.rbi +20 -0
  188. data/lib/no_response/models/shared/scope.rb +21 -0
  189. data/lib/no_response/models/shared/scope.rbi +9 -0
  190. data/lib/no_response/models/shared/security.rb +32 -0
  191. data/lib/no_response/models/shared/security.rbi +14 -0
  192. data/lib/no_response/models/shared/signal.rb +149 -0
  193. data/lib/no_response/models/shared/signal.rbi +68 -0
  194. data/lib/no_response/models/shared/signaloutcome.rb +22 -0
  195. data/lib/no_response/models/shared/signaloutcome.rbi +9 -0
  196. data/lib/no_response/models/shared/social_profile.rb +23 -0
  197. data/lib/no_response/models/shared/social_profile.rbi +9 -0
  198. data/lib/no_response/models/shared/social_profile_union.rb +23 -0
  199. data/lib/no_response/models/shared/social_profile_union.rbi +9 -0
  200. data/lib/no_response/models/shared/status.rb +20 -0
  201. data/lib/no_response/models/shared/status.rbi +9 -0
  202. data/lib/no_response/models/shared/timezone.rb +23 -0
  203. data/lib/no_response/models/shared/timezone.rbi +9 -0
  204. data/lib/no_response/models/shared/timezone_union.rb +23 -0
  205. data/lib/no_response/models/shared/timezone_union.rbi +9 -0
  206. data/lib/no_response/models/shared/transport.rb +17 -0
  207. data/lib/no_response/models/shared/transport.rbi +9 -0
  208. data/lib/no_response/models/shared/website.rb +23 -0
  209. data/lib/no_response/models/shared/website.rbi +9 -0
  210. data/lib/no_response/models/shared/website_union.rb +23 -0
  211. data/lib/no_response/models/shared/website_union.rbi +9 -0
  212. data/lib/no_response/models/shared.rb +92 -0
  213. data/lib/no_response/noresponseclient.rb +110 -0
  214. data/lib/no_response/sdk_hooks/hooks.rb +97 -0
  215. data/lib/no_response/sdk_hooks/idempotency_key_hook.rb +25 -0
  216. data/lib/no_response/sdk_hooks/registration.rb +43 -0
  217. data/lib/no_response/sdk_hooks/types.rb +178 -0
  218. data/lib/no_response/sdkconfiguration.rb +107 -0
  219. data/lib/no_response/sdkconfiguration.rbi +83 -0
  220. data/lib/no_response/utils/forms.rb +230 -0
  221. data/lib/no_response/utils/headers.rb +80 -0
  222. data/lib/no_response/utils/query_params.rb +165 -0
  223. data/lib/no_response/utils/request_bodies.rb +77 -0
  224. data/lib/no_response/utils/retries.rb +137 -0
  225. data/lib/no_response/utils/security.rb +156 -0
  226. data/lib/no_response/utils/url.rb +122 -0
  227. data/lib/no_response/utils/utils.rb +243 -0
  228. data/lib/no_response/wait.rb +66 -0
  229. data/lib/no_response/webhooks.rb +177 -0
  230. data/lib/noresponse.rb +25 -0
  231. metadata +501 -0
@@ -0,0 +1,263 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ # Auth context adapters. Generated by tools/generate_auth_adapters.py; do not edit.
5
+ #
6
+ # Each adapter maps claims your backend has already verified into the
7
+ # auth_context of an assessment request. Pass verified claims, not a raw token.
8
+ #
9
+ # The mapping is the claim map (contracts/api/v1/fixtures/assessment-contract/
10
+ # auth-context-claim-map.json), run by the small interpreter below. Every SDK
11
+ # runs the same table and the same vectors. A claim that is missing, mistyped
12
+ # or out of range maps to absent, never to a stronger assurance.
13
+ #
14
+ # Claims and overrides are hashes as JSON.parse gives them (symbol keys are
15
+ # accepted). Overrides use the auth_context field names, for example
16
+ # :email_verified. Every method raises ArgumentError when the claims have no
17
+ # valid issuer and subject or the provider is not a lowercase slug.
18
+
19
+ require 'json'
20
+ require 'time'
21
+
22
+ module NoResponse
23
+ module Auth
24
+ CLAIM_MAP_JSON = <<~'JSON'
25
+ {"version":1,"kinds":{"id":{"pattern":"[A-Za-z0-9._:@/|+=~-]+","maxLength":256},"audience":{"pattern":"[A-Za-z0-9._:/-]+","maxLength":256},"role":{"pattern":"[A-Za-z0-9._:@/-]+","maxLength":64},"method":{"pattern":"[A-Za-z0-9._:-]+","maxLength":32},"issuer":{"pattern":"https://[\\x21-\\x7e]+","maxLength":256},"time":{"pattern":"[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|[+-]([01][0-9]|2[0-3]):[0-5][0-9])","maxLength":64}},"slug":"[a-z0-9][a-z0-9._-]{1,31}","constants":{"verified":true,"transport":"backend"},"base":{"issuer":{"op":"id","kind":"issuer","claims":["iss"],"required":true},"subject":{"op":"id","kind":"id","claims":["sub"],"required":true},"audiences":{"op":"list","kind":"audience","claims":["aud"],"split":"whitespace","max":8},"issued_at":{"op":"time","claims":["iat"]},"expires_at":{"op":"time","claims":["exp"]},"auth_time":{"op":"time","claims":["auth_time"]},"session_id":{"op":"id","kind":"id","claims":["sid"]},"token_id":{"op":"id","kind":"id","claims":["jti"]}},"providers":{"workos":{"fields":{"organization_id":{"op":"id","kind":"id","claims":["org_id"]},"roles":{"op":"list","kind":"role","claims":["roles","role"],"split":"whitespace","max":32},"scopes":{"op":"list","kind":"role","claims":["permissions"],"split":"whitespace","max":32},"impersonated":{"op":"actor","claims":["act"]}}},"clerk":{"disable":["issued_at"],"fields":{"organization_id":{"op":"id","kind":"id","claims":["o.id","org_id"]},"roles":{"op":"list","kind":"role","claims":["o.rol","org_role"],"split":"none","max":32},"scopes":{"op":"list","kind":"role","claims":["o.per","org_permissions"],"split":"comma_whitespace","max":32},"impersonated":{"op":"actor","claims":["act"]},"mfa":{"op":"mfa_factor_age","claim":"fva","index":1,"unitSeconds":60,"maxSeconds":315360000}}},"auth0":{"fields":{"organization_id":{"op":"id","kind":"id","claims":["org_id"]},"scopes":{"op":"list","kind":"role","claims":["scope","permissions"],"split":"whitespace","max":32},"email_verified":{"op":"bool","claims":["email_verified"]},"phone_verified":{"op":"bool","claims":["phone_number_verified"]},"impersonated":{"op":"actor","claims":["act"]},"mfa":{"op":"mfa_methods","kind":"method","claim":"amr","max":8,"mfaValue":"mfa","ignore":["pwd"]}}},"supabase":{"disable":["auth_time"],"fields":{"session_id":{"op":"id","kind":"id","claims":["session_id"]},"roles":{"op":"list","kind":"role","claims":["role"],"split":"none","max":1},"mfa":{"op":"mfa_aal","claim":"aal","present":["aal2"],"absent":["aal1"],"amr":"amr","methodKey":"method","factors":{"totp":"totp","mfa/phone":"mfa:phone","mfa/webauthn":"mfa:webauthn","mfa/recovery_code":"mfa:recovery_code"}},"assurance_level":{"op":"marker","value":"low","when":[{"claim":"is_anonymous","equals":true},{"claim":"app_metadata.provider","equals":"anonymous"}]}}},"firebase":{"disable":["session_id","token_id"],"fields":{"tenant_id":{"op":"id","kind":"id","claims":["firebase.tenant"]},"mfa":{"op":"mfa_factor","kind":"method","claim":"firebase.sign_in_second_factor"},"assurance_level":{"op":"marker","value":"low","when":[{"claim":"firebase.sign_in_provider","equals":"anonymous"}]}}},"cognito":{"fields":{"audiences":{"op":"list","kind":"audience","claims":["aud","client_id"],"split":"none","max":8},"session_id":{"op":"id","kind":"id","claims":["origin_jti"]},"roles":{"op":"list","kind":"role","claims":["cognito:groups"],"split":"none","max":32},"scopes":{"op":"list","kind":"role","claims":["scope"],"split":"whitespace","max":32},"mfa":{"op":"mfa_methods","kind":"method","claim":"amr","max":8,"mfaValue":"mfa","ignore":["pwd"]}}},"entra":{"fields":{"tenant_id":{"op":"id","kind":"id","claims":["tid"]},"token_id":{"op":"id","kind":"id","claims":["uti"]},"roles":{"op":"list","kind":"role","claims":["roles"],"split":"none","max":32},"scopes":{"op":"list","kind":"role","claims":["scp"],"split":"whitespace","max":32},"mfa":{"op":"mfa_methods","kind":"method","claim":"amr","max":8,"mfaValue":"mfa","ignore":["pwd","none"]}}},"keycloak":{"fields":{"session_id":{"op":"id","kind":"id","claims":["sid","session_state"]},"organization_id":{"op":"id","kind":"id","claims":["organization"],"single":true},"roles":{"op":"list","kind":"role","claims":["realm_access.roles"],"split":"none","max":32},"scopes":{"op":"list","kind":"role","claims":["scope"],"split":"whitespace","max":32},"impersonated":{"op":"actor","claims":["impersonator.id","impersonator.username"],"onlyTrue":true},"mfa":{"op":"mfa_methods","kind":"method","claim":"amr","max":8,"mfaValue":"mfa","ignore":["pwd"],"classes":{"pwd":"knowledge","pin":"knowledge","kba":"knowledge","otp":"possession","hwk":"possession","swk":"possession","sc":"possession","sms":"possession","tel":"possession","fpt":"inherence","face":"inherence","iris":"inherence","retina":"inherence","vbm":"inherence"}}}},"ory":{"disable":["issuer","audiences","issued_at","token_id"],"fields":{"subject":{"op":"id","kind":"id","claims":["identity.id"],"required":true},"session_id":{"op":"id","kind":"id","claims":["id"]},"expires_at":{"op":"time","claims":["expires_at"]},"auth_time":{"op":"time","claims":["authenticated_at"]},"session_created_at":{"op":"time","claims":["issued_at"]},"account_created_at":{"op":"time","claims":["identity.created_at"]},"organization_id":{"op":"id","kind":"id","claims":["identity.organization_id"]},"mfa":{"op":"mfa_aal","claim":"authenticator_assurance_level","present":["aal2"],"absent":["aal1"],"amr":"authentication_methods","methodKey":"method","levelKey":"aal","factors":{"totp":"totp","lookup_secret":"lookup_secret","webauthn":"webauthn","code":"code"}}}},"ory_oauth2":{"fields":{"mfa":{"op":"mfa_methods","kind":"method","claim":"amr","max":8,"mfaValue":"mfa","ignore":["password","pwd"],"classes":{"password":"knowledge","totp":"possession","lookup_secret":"possession","webauthn":"possession","code":"possession"}}}},"oidc":{"slugArgument":true,"fields":{}}}}
26
+ JSON
27
+
28
+ CLAIM_MAP = JSON.parse(CLAIM_MAP_JSON).freeze
29
+ KINDS = CLAIM_MAP['kinds'].transform_values do |kind|
30
+ [Regexp.new("\\A(?:#{kind['pattern']})\\z"), kind['maxLength']]
31
+ end.freeze
32
+ SLUG = Regexp.new("\\A(?:#{CLAIM_MAP['slug']})\\z")
33
+ SPLITTERS = {
34
+ 'whitespace' => /[ \t\r\n\f\v]+/,
35
+ 'comma_whitespace' => /[ \t\r\n\f\v,]+/
36
+ }.freeze
37
+ MAX_TIME = 253_402_300_799
38
+
39
+ class << self
40
+ # Map verified WorkOS AuthKit claims plus verified user or session record overrides.
41
+ def from_workos(claims, overrides = nil)
42
+ build('workos', 'workos', claims, overrides)
43
+ end
44
+
45
+ # Map verified Clerk session claims. Pass Session.created_at as :session_created_at.
46
+ def from_clerk(claims, overrides = nil)
47
+ build('clerk', 'clerk', claims, overrides)
48
+ end
49
+
50
+ # Map verified Auth0 ID token, userinfo or normalised session claims.
51
+ # Do not pass an opaque API access token.
52
+ def from_auth0(claims, overrides = nil)
53
+ build('auth0', 'auth0', claims, overrides)
54
+ end
55
+
56
+ # Map verified Supabase Auth access token claims (legacy HS256 secret or JWKS).
57
+ # Pass the user record's contact verification as overrides.
58
+ def from_supabase(claims, overrides = nil)
59
+ build('supabase', 'supabase', claims, overrides)
60
+ end
61
+
62
+ # Map verified Firebase ID token claims from the Admin SDK verifyIdToken.
63
+ # Custom claims never change the mapping.
64
+ def from_firebase(claims, overrides = nil)
65
+ build('firebase', 'firebase', claims, overrides)
66
+ end
67
+
68
+ # Map verified Amazon Cognito user pool ID or access token claims.
69
+ # Claims added by a pre token generation Lambda never change the mapping.
70
+ def from_cognito(claims, overrides = nil)
71
+ build('cognito', 'cognito', claims, overrides)
72
+ end
73
+
74
+ # Map verified Microsoft Entra ID v1.0 or v2.0 ID or access token claims.
75
+ def from_entra(claims, overrides = nil)
76
+ build('entra', 'entra', claims, overrides)
77
+ end
78
+
79
+ # Map verified Keycloak ID or access token claims.
80
+ def from_keycloak(claims, overrides = nil)
81
+ build('keycloak', 'keycloak', claims, overrides)
82
+ end
83
+
84
+ # Map an Ory session from toSession (Kratos FrontendApi).
85
+ # Pass your project URL as the :issuer override.
86
+ def from_ory(session, overrides = nil)
87
+ build('ory', 'ory', session, overrides)
88
+ end
89
+
90
+ # Map verified Ory OAuth2 and OpenID Connect (Hydra) ID token claims.
91
+ def from_ory_oauth2(claims, overrides = nil)
92
+ build('ory_oauth2', 'ory', claims, overrides)
93
+ end
94
+
95
+ # Generic OIDC adapter for providers without a first-class helper.
96
+ # provider is a lowercase slug.
97
+ def from_oidc(provider, claims, overrides = nil)
98
+ raise ArgumentError, 'provider must be a lowercase provider slug' unless provider.is_a?(String) && SLUG.match?(provider)
99
+
100
+ build('oidc', provider, claims, overrides)
101
+ end
102
+
103
+ private
104
+
105
+ def lookup(claims, path)
106
+ current = claims
107
+ path.split('.').each do |key|
108
+ return nil unless current.is_a?(Hash) && current.key?(key)
109
+
110
+ current = current[key]
111
+ end
112
+ current
113
+ end
114
+
115
+ def number?(value)
116
+ value.is_a?(Integer) || (value.is_a?(Float) && value.finite?)
117
+ end
118
+
119
+ def matches?(kind, value)
120
+ pattern, limit = KINDS.fetch(kind)
121
+ value.is_a?(String) && value.length <= limit && pattern.match?(value)
122
+ end
123
+
124
+ def lower(value)
125
+ value.tr('A-Z', 'a-z')
126
+ end
127
+
128
+ def map_rule(claims, rule)
129
+ case rule['op']
130
+ when 'id'
131
+ rule['claims'].each do |path|
132
+ value = lookup(claims, path)
133
+ value = value[0] if rule['single'] && value.is_a?(Array) && value.length == 1
134
+ return value if matches?(rule['kind'], value)
135
+ end
136
+ nil
137
+ when 'list' then map_list(claims, rule)
138
+ when 'time' then map_time(claims, rule)
139
+ when 'bool'
140
+ rule['claims'].each do |path|
141
+ value = lookup(claims, path)
142
+ return value if [true, false].include?(value)
143
+ end
144
+ nil
145
+ when 'actor'
146
+ acting = rule['claims'].any? do |path|
147
+ value = lookup(claims, path)
148
+ if value.is_a?(Hash)
149
+ %w[sub email].any? { |key| value[key].is_a?(String) && !value[key].empty? }
150
+ else
151
+ value.is_a?(String) && !value.empty?
152
+ end
153
+ end
154
+ acting || (rule['onlyTrue'] ? nil : false)
155
+ when 'mfa_factor_age'
156
+ value = lookup(claims, rule['claim'])
157
+ age = value.is_a?(Array) ? value[rule['index']] : nil
158
+ return nil unless number?(age)
159
+
160
+ seconds = age * rule['unitSeconds']
161
+ return nil unless seconds >= 0 && seconds <= rule['maxSeconds']
162
+
163
+ { 'present' => true, 'age_seconds' => seconds.to_i }
164
+ when 'mfa_aal'
165
+ level = lookup(claims, rule['claim'])
166
+ return nil unless level.is_a?(String)
167
+ return { 'present' => false } if rule['absent'].include?(level)
168
+ return nil unless rule['present'].include?(level)
169
+
170
+ entries = lookup(claims, rule['amr'])
171
+ methods = []
172
+ (entries.is_a?(Array) ? entries : []).each do |entry|
173
+ if rule.key?('levelKey')
174
+ level_value = entry.is_a?(Hash) ? entry[rule['levelKey']] : nil
175
+ next unless level_value.is_a?(String) && rule['present'].include?(level_value)
176
+ end
177
+ name = entry.is_a?(Hash) ? entry[rule['methodKey']] : entry
178
+ mapped = name.is_a?(String) ? rule['factors'][name] : nil
179
+ methods << mapped if mapped && !methods.include?(mapped)
180
+ end
181
+ methods.empty? ? { 'present' => true } : { 'present' => true, 'methods' => methods }
182
+ when 'mfa_factor'
183
+ value = lookup(claims, rule['claim'])
184
+ matches?(rule['kind'], value) ? { 'present' => true, 'methods' => [value] } : nil
185
+ when 'marker'
186
+ hit = rule['when'].any? do |test|
187
+ value = lookup(claims, test['claim'])
188
+ value.instance_of?(test['equals'].class) && value == test['equals']
189
+ end
190
+ hit ? rule['value'] : nil
191
+ when 'mfa_methods'
192
+ value = lookup(claims, rule['claim'])
193
+ return nil unless value.is_a?(Array)
194
+
195
+ kept = value.select { |item| matches?(rule['kind'], item) }.first(rule['max'])
196
+ methods = kept.reject { |item| rule['ignore'].include?(lower(item)) }
197
+ return nil if methods.empty?
198
+
199
+ classes = rule['classes'] || {}
200
+ spanned = kept.map { |item| classes[lower(item)] }.compact.uniq
201
+ present = kept.any? { |item| lower(item) == rule['mfaValue'] } || spanned.length >= 2
202
+ { 'present' => present, 'methods' => methods }
203
+ else
204
+ raise ArgumentError, "unknown claim map op #{rule['op']}"
205
+ end
206
+ end
207
+
208
+ def map_list(claims, rule)
209
+ split = rule['split'] || 'whitespace'
210
+ rule['claims'].each do |path|
211
+ value = lookup(claims, path)
212
+ items = list_items(value, split)
213
+ kept = items.select { |item| matches?(rule['kind'], item) }.first(rule['max'])
214
+ return kept unless kept.empty?
215
+ end
216
+ nil
217
+ end
218
+
219
+ def list_items(value, split)
220
+ return value if value.is_a?(Array)
221
+ return [] unless value.is_a?(String)
222
+
223
+ split == 'none' ? [value] : value.split(SPLITTERS.fetch(split))
224
+ end
225
+
226
+ def map_time(claims, rule)
227
+ rule['claims'].each do |path|
228
+ value = lookup(claims, path)
229
+ # Whole UTC seconds; the fraction is truncated.
230
+ return Time.at(value.to_i).utc.strftime('%Y-%m-%dT%H:%M:%SZ') if number?(value) && value >= 0 && value <= MAX_TIME
231
+ return value if matches?('time', value)
232
+ end
233
+ nil
234
+ end
235
+
236
+ def normalise(value)
237
+ JSON.parse(JSON.generate(value.nil? ? {} : value))
238
+ end
239
+
240
+ def build(name, provider, raw_claims, raw_overrides)
241
+ claims = normalise(raw_claims)
242
+ overrides = normalise(raw_overrides)
243
+ spec = CLAIM_MAP['providers'].fetch(name)
244
+ rules = CLAIM_MAP['base'].merge(spec['fields'])
245
+ (spec['disable'] || []).each { |field| rules.delete(field) }
246
+ wire = { 'provider' => provider }.merge(CLAIM_MAP['constants'])
247
+ rules.each do |field, rule|
248
+ value = map_rule(claims, rule)
249
+ wire[field] = value unless value.nil?
250
+ end
251
+ overrides.each { |field, value| wire[field] = value unless value.nil? }
252
+ audiences = wire['audiences']
253
+ wire['audience'] = audiences[0] if audiences.is_a?(Array) && !audiences.empty? && !wire.key?('audience')
254
+ %w[issuer subject].each do |key|
255
+ unless wire[key].is_a?(String) && !wire[key].empty?
256
+ raise ArgumentError, 'NoResponse auth context requires a verified issuer and subject'
257
+ end
258
+ end
259
+ Models::Shared::AuthContext.from_dict(wire)
260
+ end
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,21 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ # Hash an API key your signup gate issued. Hand-written beside the generated client.
5
+
6
+ require 'digest'
7
+
8
+ module NoResponse
9
+ module IssuedCredential
10
+ # SHA-256 of an issued API key, as 64 lower-case hex characters. The key
11
+ # never leaves your backend: send only this hash, as
12
+ # issued_credential.sha256 on the assessment request. The key is hashed as
13
+ # its UTF-8 bytes exactly as given, with no trimming or normalisation.
14
+ # Raises ArgumentError for a nil or empty key.
15
+ def self.sha256(key)
16
+ raise ArgumentError, 'issued key must be a non-empty string' unless key.is_a?(String) && !key.empty?
17
+
18
+ Digest::SHA256.hexdigest(key.dup.force_encoding(Encoding::UTF_8))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,318 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "faraday"
5
+ require "faraday/multipart"
6
+ require "faraday/retry"
7
+ require "sorbet-runtime"
8
+
9
+ require_relative "sdk_hooks/hooks"
10
+ require_relative "utils/retries"
11
+
12
+ module NoResponse
13
+ extend T::Sig
14
+
15
+ class Keys
16
+ extend T::Sig
17
+
18
+ # Your own API keys.
19
+
20
+ sig { params(sdk_config: SDKConfiguration).void }
21
+ def initialize(sdk_config)
22
+ @sdk_configuration = sdk_config
23
+ end
24
+
25
+ sig { params(base_url: String, url_variables: T.nilable(T::Hash[Symbol, T.any(String, T::Enum)])).returns(String) }
26
+ def get_url(base_url:, url_variables: nil)
27
+ sd_base_url, sd_options = @sdk_configuration.get_server_details
28
+
29
+ if base_url.nil?
30
+ base_url = sd_base_url
31
+ end
32
+
33
+ if url_variables.nil?
34
+ url_variables = sd_options
35
+ end
36
+
37
+ return Utils.template_url(base_url, url_variables)
38
+ end
39
+
40
+ sig {
41
+ params(
42
+ request: Models::Shared::KeyRotationRequest,
43
+ timeout_ms: T.nilable(Integer),
44
+ http_headers: T.nilable(T::Hash[T.any(String, Symbol), String])
45
+ )
46
+ .returns(Models::Operations::RotateApiKeyResponse)
47
+ }
48
+ def rotate(request:, timeout_ms: nil, http_headers: nil)
49
+ # rotate - Rotate the API key that signs the request.
50
+ # Mints a new key with the same scopes as the key that signs the request and returns it once. The presented key keeps working for overlap_seconds (default 24 hours, from 60 seconds to 7 days), then answers 401. A key accepts one rotation: rotating again with a key already in its overlap answers 409 key_rotation_in_progress, so rotate with the new key. Rotation is not idempotent: a retry after a lost answer mints another key.
51
+ url, params = @sdk_configuration.get_server_details
52
+ base_url = Utils.template_url(url, params)
53
+ url = "#{base_url}/v1/keys/rotate"
54
+ headers = {}
55
+ headers = T.cast(headers, T::Hash[String, String])
56
+ req_content_type, data, form = Utils.serialize_request_body(request, false, false, :request, :json)
57
+ headers["content-type"] = req_content_type
58
+ raise StandardError, "request body is required" if data.nil? && form.nil?
59
+
60
+ if form && !form.empty?
61
+ body = Utils.encode_form(form)
62
+ elsif Utils.match_content_type(req_content_type, "application/x-www-form-urlencoded")
63
+ body = URI.encode_www_form(T.cast(data, T::Hash[Symbol, Object]))
64
+ else
65
+ body = data
66
+ end
67
+
68
+ headers["Accept"] = "application/json"
69
+ headers["user-agent"] = @sdk_configuration.user_agent
70
+
71
+ security = @sdk_configuration.security_source&.call
72
+
73
+ timeout = (timeout_ms.to_f / 1000) unless timeout_ms.nil?
74
+ timeout ||= @sdk_configuration.timeout
75
+
76
+ connection = @sdk_configuration.client
77
+
78
+ hook_ctx = SDKHooks::HookContext.new(
79
+ config: @sdk_configuration,
80
+ base_url: base_url,
81
+ oauth2_scopes: nil,
82
+ operation_id: "rotateApiKey",
83
+ security_source: @sdk_configuration.security_source
84
+ )
85
+
86
+ error = T.let(nil, T.nilable(StandardError))
87
+ http_response = T.let(nil, T.nilable(Faraday::Response))
88
+
89
+ begin
90
+ http_response = T.must(connection).post(url) do |req|
91
+ req.body = body
92
+ req.headers.merge!(headers)
93
+ req.options.timeout = timeout unless timeout.nil?
94
+ Utils.configure_request_security(req, security)
95
+ http_headers&.each do |key, value|
96
+ req.headers[key.to_s] = value
97
+ end
98
+
99
+ @sdk_configuration.hooks.before_request(
100
+ hook_ctx: SDKHooks::BeforeRequestHookContext.new(
101
+ hook_ctx: hook_ctx
102
+ ),
103
+ request: req
104
+ )
105
+ end
106
+
107
+ rescue StandardError => e
108
+ error = e
109
+ ensure
110
+ if http_response.nil? || Utils.error_status?(http_response.status)
111
+ http_response = @sdk_configuration.hooks.after_error(
112
+ error: error,
113
+ hook_ctx: SDKHooks::AfterErrorHookContext.new(
114
+ hook_ctx: hook_ctx
115
+ ),
116
+ response: http_response
117
+ )
118
+ else
119
+ http_response = @sdk_configuration.hooks.after_success(
120
+ hook_ctx: SDKHooks::AfterSuccessHookContext.new(
121
+ hook_ctx: hook_ctx
122
+ ),
123
+ response: http_response
124
+ )
125
+ end
126
+
127
+ if http_response.nil?
128
+ raise error if !error.nil?
129
+ raise "no response"
130
+ end
131
+ end
132
+
133
+ content_type = http_response.headers.fetch("Content-Type", "application/octet-stream")
134
+ if Utils.match_status_code(http_response.status, ["201"])
135
+ if Utils.match_content_type(content_type, "application/json")
136
+ @sdk_configuration.hooks.after_success(
137
+ hook_ctx: SDKHooks::AfterSuccessHookContext.new(
138
+ hook_ctx: hook_ctx
139
+ ),
140
+ response: http_response
141
+ )
142
+ response_data = http_response.env.response_body
143
+ obj = Crystalline.unmarshal_json(JSON.parse(response_data), Models::Shared::KeyRotation)
144
+ result = obj
145
+ response = Models::Operations::RotateApiKeyResponse.new(
146
+ result: result,
147
+ headers: http_response.headers
148
+ )
149
+
150
+ return response
151
+ else
152
+ raise(
153
+ ::NoResponse::Models::Errors::APIStatusError.new(
154
+ status_code: http_response.status,
155
+ body: http_response.env.response_body,
156
+ raw_response: http_response
157
+ ),
158
+ "Unknown content type received"
159
+ )
160
+ end
161
+ elsif Utils.match_status_code(http_response.status, ["400"])
162
+ if Utils.match_content_type(content_type, "application/problem+json")
163
+ @sdk_configuration.hooks.after_success(
164
+ hook_ctx: SDKHooks::AfterSuccessHookContext.new(
165
+ hook_ctx: hook_ctx
166
+ ),
167
+ response: http_response
168
+ )
169
+ response_data = http_response.env.response_body
170
+ obj = Crystalline.unmarshal_json(JSON.parse(response_data), Models::Errors::BadRequestError)
171
+ raise obj
172
+ else
173
+ raise(
174
+ ::NoResponse::Models::Errors::APIStatusError.new(
175
+ status_code: http_response.status,
176
+ body: http_response.env.response_body,
177
+ raw_response: http_response
178
+ ),
179
+ "Unknown content type received"
180
+ )
181
+ end
182
+ elsif Utils.match_status_code(http_response.status, ["401"])
183
+ if Utils.match_content_type(content_type, "application/problem+json")
184
+ @sdk_configuration.hooks.after_success(
185
+ hook_ctx: SDKHooks::AfterSuccessHookContext.new(
186
+ hook_ctx: hook_ctx
187
+ ),
188
+ response: http_response
189
+ )
190
+ response_data = http_response.env.response_body
191
+ obj = Crystalline.unmarshal_json(JSON.parse(response_data), Models::Errors::AuthenticationError)
192
+ raise obj
193
+ else
194
+ raise(
195
+ ::NoResponse::Models::Errors::APIStatusError.new(
196
+ status_code: http_response.status,
197
+ body: http_response.env.response_body,
198
+ raw_response: http_response
199
+ ),
200
+ "Unknown content type received"
201
+ )
202
+ end
203
+ elsif Utils.match_status_code(http_response.status, ["403"])
204
+ if Utils.match_content_type(content_type, "application/problem+json")
205
+ @sdk_configuration.hooks.after_success(
206
+ hook_ctx: SDKHooks::AfterSuccessHookContext.new(
207
+ hook_ctx: hook_ctx
208
+ ),
209
+ response: http_response
210
+ )
211
+ response_data = http_response.env.response_body
212
+ obj = Crystalline.unmarshal_json(JSON.parse(response_data), Models::Errors::PermissionDeniedError)
213
+ raise obj
214
+ else
215
+ raise(
216
+ ::NoResponse::Models::Errors::APIStatusError.new(
217
+ status_code: http_response.status,
218
+ body: http_response.env.response_body,
219
+ raw_response: http_response
220
+ ),
221
+ "Unknown content type received"
222
+ )
223
+ end
224
+ elsif Utils.match_status_code(http_response.status, ["404"])
225
+ if Utils.match_content_type(content_type, "application/problem+json")
226
+ @sdk_configuration.hooks.after_success(
227
+ hook_ctx: SDKHooks::AfterSuccessHookContext.new(
228
+ hook_ctx: hook_ctx
229
+ ),
230
+ response: http_response
231
+ )
232
+ response_data = http_response.env.response_body
233
+ obj = Crystalline.unmarshal_json(JSON.parse(response_data), Models::Errors::NotFoundError)
234
+ raise obj
235
+ else
236
+ raise(
237
+ ::NoResponse::Models::Errors::APIStatusError.new(
238
+ status_code: http_response.status,
239
+ body: http_response.env.response_body,
240
+ raw_response: http_response
241
+ ),
242
+ "Unknown content type received"
243
+ )
244
+ end
245
+ elsif Utils.match_status_code(http_response.status, ["409"])
246
+ if Utils.match_content_type(content_type, "application/problem+json")
247
+ @sdk_configuration.hooks.after_success(
248
+ hook_ctx: SDKHooks::AfterSuccessHookContext.new(
249
+ hook_ctx: hook_ctx
250
+ ),
251
+ response: http_response
252
+ )
253
+ response_data = http_response.env.response_body
254
+ obj = Crystalline.unmarshal_json(JSON.parse(response_data), Models::Errors::ConflictError)
255
+ raise obj
256
+ else
257
+ raise(
258
+ ::NoResponse::Models::Errors::APIStatusError.new(
259
+ status_code: http_response.status,
260
+ body: http_response.env.response_body,
261
+ raw_response: http_response
262
+ ),
263
+ "Unknown content type received"
264
+ )
265
+ end
266
+ elsif Utils.match_status_code(http_response.status, ["429"])
267
+ if Utils.match_content_type(content_type, "application/problem+json")
268
+ @sdk_configuration.hooks.after_success(
269
+ hook_ctx: SDKHooks::AfterSuccessHookContext.new(
270
+ hook_ctx: hook_ctx
271
+ ),
272
+ response: http_response
273
+ )
274
+ response_data = http_response.env.response_body
275
+ obj = Crystalline.unmarshal_json(JSON.parse(response_data), Models::Errors::RateLimitError)
276
+ raise obj
277
+ else
278
+ raise(
279
+ ::NoResponse::Models::Errors::APIStatusError.new(
280
+ status_code: http_response.status,
281
+ body: http_response.env.response_body,
282
+ raw_response: http_response
283
+ ),
284
+ "Unknown content type received"
285
+ )
286
+ end
287
+ elsif Utils.match_status_code(http_response.status, ["4XX"])
288
+ raise(
289
+ ::NoResponse::Models::Errors::APIStatusError.new(
290
+ status_code: http_response.status,
291
+ body: http_response.env.response_body,
292
+ raw_response: http_response
293
+ ),
294
+ "API error occurred"
295
+ )
296
+ elsif Utils.match_status_code(http_response.status, ["5XX"])
297
+ raise(
298
+ ::NoResponse::Models::Errors::APIStatusError.new(
299
+ status_code: http_response.status,
300
+ body: http_response.env.response_body,
301
+ raw_response: http_response
302
+ ),
303
+ "API error occurred"
304
+ )
305
+ else
306
+ raise(
307
+ ::NoResponse::Models::Errors::APIStatusError.new(
308
+ status_code: http_response.status,
309
+ body: http_response.env.response_body,
310
+ raw_response: http_response
311
+ ),
312
+ "Unknown status code received"
313
+ )
314
+
315
+ end
316
+ end
317
+ end
318
+ end