this repo has no description

add andrid

+1 -1
.eslintrc.js
··· 1 1 // @generated by expo-module-scripts 2 - module.exports = require('expo-module-scripts/eslintrc.base.js') 2 + module.exports = require('expo-module-scripts/eslintrc.base.js');
+2
android/.editorconfig
··· 1 + [*.{kt,kts}] 2 + indent_size=2
+4
android/build.gradle
··· 41 41 abortOnError false 42 42 } 43 43 } 44 + 45 + dependencies { 46 + implementation "com.nimbusds:nimbus-jose-jwt:10.3.1" 47 + }
+70
android/src/main/java/expo/modules/atprotoauth/Crypto.kt
··· 1 + package expo.modules.expoatprotoauth 2 + 3 + import com.nimbusds.jose.Algorithm 4 + import com.nimbusds.jose.jwk.Curve 5 + import com.nimbusds.jose.jwk.ECKey 6 + import com.nimbusds.jose.jwk.KeyUse 7 + import com.nimbusds.jose.util.Base64URL 8 + import expo.modules.atprotoauth.EncodedJWK 9 + import java.security.KeyPairGenerator 10 + import java.security.MessageDigest 11 + import java.security.interfaces.ECPrivateKey 12 + import java.security.interfaces.ECPublicKey 13 + import java.util.UUID 14 + 15 + class Crypto { 16 + fun digest(data: ByteArray): ByteArray { 17 + val instance = MessageDigest.getInstance("sha256") 18 + return instance.digest(data) 19 + } 20 + 21 + fun getRandomValues(byteLength: Int): ByteArray { 22 + val random = ByteArray(byteLength) 23 + java.security.SecureRandom().nextBytes(random) 24 + return random 25 + } 26 + 27 + fun generateJwk(): EncodedJWK { 28 + val keyIdString = UUID.randomUUID().toString() 29 + 30 + val keyPairGen = KeyPairGenerator.getInstance("EC") 31 + keyPairGen.initialize(Curve.P_256.toECParameterSpec()) 32 + val keyPair = keyPairGen.generateKeyPair() 33 + 34 + val publicKey = keyPair.public as ECPublicKey 35 + val privateKey = keyPair.private as ECPrivateKey 36 + 37 + val privateJwk = 38 + ECKey 39 + .Builder(Curve.P_256, publicKey) 40 + .privateKey(privateKey) 41 + .keyUse(KeyUse.SIGNATURE) 42 + .keyID(keyIdString) 43 + .algorithm(Algorithm.parse("ES256")) 44 + .build() 45 + 46 + return EncodedJWK().apply { 47 + kty = privateJwk.keyType.value 48 + use = "sig" 49 + crv = privateJwk.curve.toString() 50 + kid = keyIdString 51 + x = privateJwk.x.toString() 52 + y = privateJwk.y.toString() 53 + d = privateJwk.d.toString() 54 + alg = privateJwk.algorithm.name 55 + } 56 + } 57 + 58 + fun decodeJwk(encodedJwk: EncodedJWK): ECKey { 59 + val xb64url = Base64URL.from(encodedJwk.x) 60 + val yb64url = Base64URL.from(encodedJwk.y) 61 + val db64url = Base64URL.from(encodedJwk.d) 62 + return ECKey 63 + .Builder(Curve.P_256, xb64url, yb64url) 64 + .d(db64url) 65 + .keyUse(KeyUse.SIGNATURE) 66 + .keyID(encodedJwk.kid) 67 + .algorithm(Algorithm.parse(encodedJwk.alg)) 68 + .build() 69 + } 70 + }
+27 -36
android/src/main/java/expo/modules/atprotoauth/ExpoAtprotoAuthModule.kt
··· 1 1 package expo.modules.atprotoauth 2 2 3 + import expo.modules.expoatprotoauth.Crypto 4 + import expo.modules.expoatprotoauth.Jose 3 5 import expo.modules.kotlin.modules.Module 4 6 import expo.modules.kotlin.modules.ModuleDefinition 5 - import java.net.URL 6 7 7 8 class ExpoAtprotoAuthModule : Module() { 8 - // Each module class must implement the definition function. The definition consists of components 9 - // that describes the module's functionality and behavior. 10 - // See https://docs.expo.dev/modules/module-api for more details about available components. 11 - override fun definition() = ModuleDefinition { 12 - // Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument. 13 - // Can be inferred from module's class name, but it's recommended to set it explicitly for clarity. 14 - // The module will be accessible from `requireNativeModule('ExpoAtprotoAuth')` in JavaScript. 15 - Name("ExpoAtprotoAuth") 9 + override fun definition() = 10 + ModuleDefinition { 11 + Name("ExpoAtprotoAuth") 16 12 17 - // Sets constant properties on the module. Can take a dictionary or a closure that returns a dictionary. 18 - Constants( 19 - "PI" to Math.PI 20 - ) 13 + Function("digest") { data: ByteArray, algo: String -> 14 + if (algo != "sha256") { 15 + throw IllegalArgumentException("Unsupported algorithm: $algo") 16 + } 17 + return@Function Crypto().digest(data) 18 + } 21 19 22 - // Defines event names that the module can send to JavaScript. 23 - Events("onChange") 20 + Function("getRandomValues") { byteLength: Int -> 21 + return@Function Crypto().getRandomValues(byteLength) 22 + } 24 23 25 - // Defines a JavaScript synchronous function that runs the native code on the JavaScript thread. 26 - Function("hello") { 27 - "Hello world! 👋" 28 - } 24 + Function("generatePrivateJwk") { algo: String -> 25 + if (algo != "ES256") { 26 + throw IllegalArgumentException("Unsupported algorithm: $algo") 27 + } 28 + return@Function Crypto().generateJwk() 29 + } 29 30 30 - // Defines a JavaScript function that always returns a Promise and whose native code 31 - // is by default dispatched on the different thread than the JavaScript runtime runs on. 32 - AsyncFunction("setValueAsync") { value: String -> 33 - // Send an event to JavaScript. 34 - sendEvent("onChange", mapOf( 35 - "value" to value 36 - )) 37 - } 31 + Function("createJwt") { header: String, payload: String, encodedJwk: EncodedJWK -> 32 + val jwk = Crypto().decodeJwk(encodedJwk) 33 + return@Function Jose().createJwt(header, payload, jwk) 34 + } 38 35 39 - // Enables the module to be used as a native view. Definition components that are accepted as part of 40 - // the view definition: Prop, Events. 41 - View(ExpoAtprotoAuthView::class) { 42 - // Defines a setter for the `url` prop. 43 - Prop("url") { view: ExpoAtprotoAuthView, url: URL -> 44 - view.webView.loadUrl(url.toString()) 36 + Function("verifyJwt") { token: String, encodedJwk: EncodedJWK, options: VerifyOptions -> 37 + val jwk = Crypto().decodeJwk(encodedJwk) 38 + return@Function Jose().verifyJwt(token, jwk, options) 45 39 } 46 - // Defines an event that the view can send to JavaScript. 47 - Events("onLoad") 48 40 } 49 - } 50 41 }
-30
android/src/main/java/expo/modules/atprotoauth/ExpoAtprotoAuthView.kt
··· 1 - package expo.modules.atprotoauth 2 - 3 - import android.content.Context 4 - import android.webkit.WebView 5 - import android.webkit.WebViewClient 6 - import expo.modules.kotlin.AppContext 7 - import expo.modules.kotlin.viewevent.EventDispatcher 8 - import expo.modules.kotlin.views.ExpoView 9 - 10 - class ExpoAtprotoAuthView(context: Context, appContext: AppContext) : ExpoView(context, appContext) { 11 - // Creates and initializes an event dispatcher for the `onLoad` event. 12 - // The name of the event is inferred from the value and needs to match the event name defined in the module. 13 - private val onLoad by EventDispatcher() 14 - 15 - // Defines a WebView that will be used as the root subview. 16 - internal val webView = WebView(context).apply { 17 - layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) 18 - webViewClient = object : WebViewClient() { 19 - override fun onPageFinished(view: WebView, url: String) { 20 - // Sends an event to JavaScript. Triggers a callback defined on the view component in JavaScript. 21 - onLoad(mapOf("url" to url)) 22 - } 23 - } 24 - } 25 - 26 - init { 27 - // Adds the WebView to the view hierarchy. 28 - addView(webView) 29 - } 30 - }
+116
android/src/main/java/expo/modules/atprotoauth/Jose.kt
··· 1 + package expo.modules.expoatprotoauth 2 + 3 + import com.nimbusds.jose.JWSHeader 4 + import com.nimbusds.jose.crypto.ECDSASigner 5 + import com.nimbusds.jose.crypto.ECDSAVerifier 6 + import com.nimbusds.jose.jwk.ECKey 7 + import com.nimbusds.jwt.JWTClaimsSet 8 + import com.nimbusds.jwt.SignedJWT 9 + import expo.modules.atprotoauth.VerifyOptions 10 + import expo.modules.atprotoauth.VerifyResult 11 + 12 + class InvalidPayloadException( 13 + message: String, 14 + ) : Exception(message) 15 + 16 + class Jose { 17 + fun createJwt( 18 + header: String, 19 + payload: String, 20 + jwk: ECKey, 21 + ): String { 22 + val parsedHeader = JWSHeader.parse(header) 23 + val parsedPayload = JWTClaimsSet.parse(payload) 24 + 25 + val signer = ECDSASigner(jwk) 26 + val jwt = SignedJWT(parsedHeader, parsedPayload) 27 + jwt.sign(signer) 28 + 29 + return jwt.serialize() 30 + } 31 + 32 + fun verifyJwt( 33 + token: String, 34 + jwk: ECKey, 35 + options: VerifyOptions, 36 + ): VerifyResult { 37 + val jwt = SignedJWT.parse(token) 38 + val verifier = ECDSAVerifier(jwk) 39 + 40 + if (!jwt.verify(verifier)) { 41 + throw InvalidPayloadException("invalid JWT signature") 42 + } 43 + 44 + val protectedHeader = emptyMap<String, Any>().toMutableMap() 45 + protectedHeader["alg"] = jwt.header.algorithm 46 + 47 + jwt.header.getCustomParam("jku")?.let { 48 + protectedHeader["jku"] = it.toString() 49 + } 50 + jwt.header.keyID?.let { 51 + protectedHeader["kid"] = it 52 + } 53 + jwt.header.type?.let { 54 + protectedHeader["typ"] = it.toString() 55 + } 56 + jwt.header.contentType?.let { 57 + protectedHeader["cty"] = it 58 + } 59 + jwt.header.criticalParams?.let { 60 + protectedHeader["crit"] = it.toList() 61 + } 62 + 63 + options.typ?.let { 64 + if (jwt.header.type.toString() != it) { 65 + throw InvalidPayloadException("typ mismatch") 66 + } 67 + } 68 + 69 + val claims = jwt.jwtClaimsSet 70 + 71 + options.requiredClaims?.let { requiredClaims -> 72 + requiredClaims.forEach { claim -> 73 + if (!claims.claims.containsKey(claim)) { 74 + throw InvalidPayloadException("required claim '$claim' missing") 75 + } 76 + } 77 + } 78 + 79 + options.audience?.let { 80 + if (!claims.audience.contains(it)) { 81 + throw InvalidPayloadException("audience mismatch") 82 + } 83 + } 84 + 85 + options.subject?.let { 86 + if (claims.subject != it) { 87 + throw InvalidPayloadException("subject mismatch") 88 + } 89 + } 90 + 91 + options.checkTolerance?.let { 92 + val currentTime = options.currentDate ?: (System.currentTimeMillis() / 1000.0) 93 + if (claims.issueTime.time / 1000.0 + it < currentTime) { 94 + throw InvalidPayloadException("token expired") 95 + } 96 + } 97 + 98 + options.maxTokenAge?.let { 99 + val currentTime = options.currentDate ?: (System.currentTimeMillis() / 1000.0) 100 + if (claims.issueTime.time / 1000.0 + it < currentTime) { 101 + throw InvalidPayloadException("token expired") 102 + } 103 + } 104 + 105 + options.issuer?.let { 106 + if (claims.issuer != it) { 107 + throw InvalidPayloadException("issuer mismatch") 108 + } 109 + } 110 + 111 + return VerifyResult().apply { 112 + payload = jwt.payload.toString() 113 + this.protectedHeader = protectedHeader 114 + } 115 + } 116 + }
+64
android/src/main/java/expo/modules/atprotoauth/Records.kt
··· 1 + package expo.modules.atprotoauth 2 + 3 + import expo.modules.kotlin.records.Field 4 + import expo.modules.kotlin.records.Record 5 + 6 + class EncodedJWK : Record { 7 + @Field 8 + var kty: String = "" 9 + 10 + @Field 11 + var use: String = "" 12 + 13 + @Field 14 + var crv: String = "" 15 + 16 + @Field 17 + var kid: String = "" 18 + 19 + @Field 20 + var x: String = "" 21 + 22 + @Field 23 + var y: String = "" 24 + 25 + @Field 26 + var d: String = "" 27 + 28 + @Field 29 + var alg: String = "" 30 + } 31 + 32 + class VerifyOptions : Record { 33 + @Field 34 + var audience: String? = null 35 + 36 + @Field 37 + var checkTolerance: Double? = null 38 + 39 + @Field 40 + var issuer: String? = null 41 + 42 + @Field 43 + var maxTokenAge: Double? = null 44 + 45 + @Field 46 + var subject: String? = null 47 + 48 + @Field 49 + var typ: String? = null 50 + 51 + @Field 52 + var currentDate: Double? = null 53 + 54 + @Field 55 + var requiredClaims: Array<String>? = null 56 + } 57 + 58 + class VerifyResult : Record { 59 + @Field 60 + var payload: String = "" 61 + 62 + @Field 63 + var protectedHeader: Map<String, Any> = emptyMap() 64 + }
+21 -28
example/App.tsx
··· 1 1 import React from 'react' 2 - import { Text, View, StyleSheet, Button, Alert, TextInput } from 'react-native' 2 + import { 3 + Text, 4 + View, 5 + StyleSheet, 6 + Button, 7 + Alert, 8 + TextInput, 9 + Platform, 10 + } from 'react-native' 3 11 import { 4 12 digest, 5 13 getRandomValues, ··· 10 18 import { OAuthSession } from '@atproto/oauth-client' 11 19 import { Agent } from '@atproto/api' 12 20 import type { ReactNativeKey } from 'expo-atproto-auth' 13 - import * as Browser from 'expo-web-browser' 14 21 15 22 const client = new ReactNativeOAuthClient({ 16 23 clientMetadata: { ··· 109 116 <Button 110 117 title="Open Sign In" 111 118 onPress={async () => { 112 - let url: URL 113 - try { 114 - url = await client.authorize(input ?? '') 115 - } catch (e: any) { 116 - Alert.alert('Error', e.toString()) 117 - return 118 - } 119 - const res = await Browser.openAuthSessionAsync( 120 - url.toString(), 121 - 'at.hailey://auth/callback' 122 - ) 123 - 124 - if (res.type === 'success') { 125 - const resUrl = new URL(res.url) 126 - try { 127 - const params = new URLSearchParams(resUrl.hash.substring(1)) 128 - const callbackRes = await client.callback(params) 129 - setSession(callbackRes.session) 130 - 131 - const newAgent = new Agent(callbackRes.session) 132 - setAgent(newAgent) 133 - } catch (e: any) { 134 - Alert.alert('Error', e.toString()) 135 - } 119 + const res = await client.signIn(input ?? '') 120 + if (res.status === 'success') { 121 + setSession(res.session) 122 + const newAgent = new Agent(res.session) 123 + setAgent(newAgent) 124 + } else if (res.status === 'error') { 125 + Alert.alert('Error', (res.error as any).toString()) 136 126 } else { 137 - Alert.alert('Error', `Received non-success status: ${res.type}`) 127 + Alert.alert( 128 + 'Error', 129 + `Received unknown WebResultType: ${res.status}` 130 + ) 138 131 } 139 132 }} 140 133 /> ··· 177 170 onPress={async () => { 178 171 try { 179 172 await agent?.post({ 180 - text: 'Test post from Expo Atproto Auth example', 173 + text: `Test post from Expo Atproto Auth example using platform ${Platform.OS}`, 181 174 }) 182 175 } catch (e: any) { 183 176 Alert.alert('Error', e.toString())
+2 -2
example/ios/expoatprotoauthexample.xcodeproj/project.pbxproj
··· 351 351 ); 352 352 OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; 353 353 PRODUCT_BUNDLE_IDENTIFIER = expo.modules.atprotoauth.example; 354 - PRODUCT_NAME = expoatprotoauthexample; 354 + PRODUCT_NAME = "expoatprotoauthexample"; 355 355 SWIFT_OBJC_BRIDGING_HEADER = "expoatprotoauthexample/expoatprotoauthexample-Bridging-Header.h"; 356 356 SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 357 357 SWIFT_VERSION = 5.0; ··· 382 382 ); 383 383 OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; 384 384 PRODUCT_BUNDLE_IDENTIFIER = expo.modules.atprotoauth.example; 385 - PRODUCT_NAME = expoatprotoauthexample; 385 + PRODUCT_NAME = "expoatprotoauthexample"; 386 386 SWIFT_OBJC_BRIDGING_HEADER = "expoatprotoauthexample/expoatprotoauthexample-Bridging-Header.h"; 387 387 SWIFT_VERSION = 5.0; 388 388 TARGETED_DEVICE_FAMILY = "1,2";
+3 -3
ios/Crypto.swift
··· 13 13 return Data(bytes) 14 14 } 15 15 16 - static func generateJwk() -> JWK { 16 + static func generateJwk() -> EncodedJWK { 17 17 let kid = UUID().uuidString 18 18 19 19 let privKey = P256.Signing.PrivateKey() ··· 23 23 let y = pubKey.x963Representation[33...].base64URLEncodedString() 24 24 let d = privKey.rawRepresentation.base64URLEncodedString() 25 25 26 - let jwk = JWK() 26 + let jwk = EncodedJWK() 27 27 jwk.kty = "EC" 28 28 jwk.use = "sig" 29 29 jwk.crv = "P-256" ··· 36 36 return jwk 37 37 } 38 38 39 - static func importJwk(x: String, y: String, d: String) throws -> SecKey { 39 + static func decodeJwk(x: String, y: String, d: String) throws -> SecKey { 40 40 func base64UrlDecode(_ string: String) -> Data? { 41 41 var base64 = string 42 42 .replacingOccurrences(of: "-", with: "+")
+7 -13
ios/ExpoAtprotoAuthModule.swift
··· 23 23 return CryptoUtil.getRandomValues(byteLength: byteLength) 24 24 } 25 25 26 - Function("generatePrivateJwk") { (algo: String) throws -> JWK in 26 + Function("generatePrivateJwk") { (algo: String) throws -> EncodedJWK in 27 27 if algo != "ES256" { 28 28 throw ExpoAtprotoAuthError.unsupportedAlgorithm(algo) 29 29 } 30 30 return CryptoUtil.generateJwk() 31 31 } 32 32 33 - Function("createJwt") { (header: String, payload: String, jwk: JWK) throws -> String in 34 - let key = try CryptoUtil.importJwk(x: jwk.x, y: jwk.y, d: jwk.d) 35 - let jwt = try JoseUtil.createJwt(header: header, payload: payload, jwk: key) 33 + Function("createJwt") { (header: String, payload: String, jwk: EncodedJWK) throws -> String in 34 + let jwk = try CryptoUtil.decodeJwk(x: jwk.x, y: jwk.y, d: jwk.d) 35 + let jwt = try JoseUtil.createJwt(header: header, payload: payload, jwk: jwk) 36 36 return jwt 37 37 } 38 38 39 - Function("verifyJwt") { (token: String, jwk: JWK, options: VerifyOptions) throws -> VerifyResponse in 40 - let key = try CryptoUtil.importJwk(x: jwk.x, y: jwk.y, d: jwk.d) 41 - let res = try JoseUtil.verifyJwt(token: token, jwk: key, options: options) 39 + Function("verifyJwt") { (token: String, jwk: EncodedJWK, options: VerifyOptions) throws -> VerifyResult in 40 + let jwk = try CryptoUtil.decodeJwk(x: jwk.x, y: jwk.y, d: jwk.d) 41 + let res = try JoseUtil.verifyJwt(token: token, jwk: jwk, options: options) 42 42 return res 43 - } 44 - 45 - AsyncFunction("setValueAsync") { (value: String) in 46 - self.sendEvent("onChange", [ 47 - "value": value 48 - ]) 49 43 } 50 44 } 51 45 }
+2 -2
ios/Jose.swift
··· 35 35 return jws.compactSerializedString 36 36 } 37 37 38 - static func verifyJwt(token: String, jwk: SecKey, options: VerifyOptions) throws -> VerifyResponse { 38 + static func verifyJwt(token: String, jwk: SecKey, options: VerifyOptions) throws -> VerifyResult { 39 39 guard let jws = try? JWS(compactSerialization: token), 40 40 let verifier = Verifier(verifyingAlgorithm: .ES256, key: jwk), 41 41 let validation = try? jws.validate(using: verifier) ··· 128 128 } 129 129 } 130 130 131 - let res = VerifyResponse() 131 + let res = VerifyResult() 132 132 res.payload = payload 133 133 res.protectedHeader = protectedHeader 134 134
+2 -2
ios/Records.swift
··· 1 1 import ExpoModulesCore 2 2 3 - struct JWK: Record { 3 + struct EncodedJWK: Record { 4 4 @Field 5 5 var kty: String 6 6 ··· 52 52 var requiredClaims: [String]? 53 53 } 54 54 55 - struct VerifyResponse: Record { 55 + struct VerifyResult: Record { 56 56 @Field 57 57 var payload: String 58 58
+3 -1
package.json
··· 8 8 "build": "expo-module build", 9 9 "clean": "expo-module clean", 10 10 "typecheck": "tsc", 11 - "lint": "eslint \"**/*.{js,ts,tsx}\"", 11 + "lint": "eslint \"**/*.{ts,tsx}\"", 12 12 "test": "expo-module test", 13 13 "prepare": "expo-module prepare", 14 14 "prepublishOnly": "expo-module prepublishOnly", ··· 43 43 "eslint-plugin-prettier": "^5.2.3", 44 44 "expo": "~53.0.0", 45 45 "expo-module-scripts": "^4.1.9", 46 + "expo-web-browser": "^14.2.0", 46 47 "jest": "^29.7.0", 47 48 "patch-package": "^8.0.0", 48 49 "postinstall-postinstall": "^2.1.0", ··· 53 54 "peerDependencies": { 54 55 "@atproto/oauth-client": "*", 55 56 "expo": "*", 57 + "expo-web-browser": "*", 56 58 "react": "*", 57 59 "react-native": "*", 58 60 "react-native-mmkv": "*"
+1 -1
src/ExpoAtprotoAuth.types.ts
··· 23 23 requiredClaims?: string[] 24 24 } 25 25 26 - export type VerifyResponse = { 26 + export type VerifyResult = { 27 27 payload: string 28 28 protectedHeader: JwtHeader 29 29 }
+1 -1
src/index.ts
··· 8 8 verifyJwt, 9 9 ReactNativeKey, 10 10 } from './react-native-key' 11 - export { ReactNativeOAuthClient } from './react-native-oauth-client' 11 + export { ExpoOAuthClient as ReactNativeOAuthClient } from './react-native-oauth-client'
+35 -1
src/react-native-oauth-client.ts
··· 5 5 type OAuthResponseMode, 6 6 atprotoLoopbackClientMetadata, 7 7 OAuthClient, 8 + OAuthSession, 8 9 } from '@atproto/oauth-client' 9 10 import { ReactNativeRuntimeImplementation } from './react-native-runtime-implementation' 10 11 import { ReactNativeOAuthDatabase } from './react-native-oauth-database' 12 + import { openAuthSessionAsync, WebBrowserResultType } from 'expo-web-browser' 11 13 12 14 export type Simplify<T> = { [K in keyof T]: T[K] } & NonNullable<unknown> 13 15 ··· 33 35 > 34 36 > 35 37 36 - export class ReactNativeOAuthClient extends OAuthClient { 38 + export class ExpoOAuthClient extends OAuthClient { 37 39 constructor({ 38 40 responseMode = 'fragment', 39 41 ...options ··· 65 67 protectedResourceMetadataCache: 66 68 database.getProtectedResourceMetadataCache(), 67 69 }) 70 + } 71 + 72 + async signIn( 73 + input: string 74 + ): Promise< 75 + | { status: WebBrowserResultType } 76 + | { status: 'error'; error: unknown } 77 + | { status: 'success'; session: OAuthSession } 78 + > { 79 + let url: URL 80 + try { 81 + url = await this.authorize(input) 82 + } catch (e: unknown) { 83 + return { status: 'error', error: e } 84 + } 85 + 86 + const res = await openAuthSessionAsync( 87 + url.toString(), 88 + this.clientMetadata.redirect_uris[0], 89 + { 90 + createTask: false, 91 + } 92 + ) 93 + 94 + if (res.type === 'success') { 95 + const resUrl = new URL(res.url) 96 + const params = new URLSearchParams(resUrl.hash.substring(1)) 97 + const callbackRes = await this.callback(params) 98 + return { status: 'success', session: callbackRes.session } 99 + } else { 100 + return { status: res.type } 101 + } 68 102 } 69 103 }
+5
yarn.lock
··· 4350 4350 dependencies: 4351 4351 invariant "^2.2.4" 4352 4352 4353 + expo-web-browser@^14.2.0: 4354 + version "14.2.0" 4355 + resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-14.2.0.tgz#d8fb521ae349aebbf5c0ca32448877480124c06c" 4356 + integrity sha512-6S51d8pVlDRDsgGAp8BPpwnxtyKiMWEFdezNz+5jVIyT+ctReW42uxnjRgtsdn5sXaqzhaX+Tzk/CWaKCyC0hw== 4357 + 4353 4358 expo@~53.0.0: 4354 4359 version "53.0.19" 4355 4360 resolved "https://registry.yarnpkg.com/expo/-/expo-53.0.19.tgz#69f8ed224efadf517d3ff66dde3d536fb3e8f00a"