Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Deauthorize Requests/Event to React Native #505

Merged
merged 1 commit into from
Jul 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ import {
MWASessionEvent,
MWASessionEventType,
resolve,
ReauthorizeDappResponse,
ReuthorizeDappCompleteResponse,
DeauthorizeDappResponse,
MWARequestFailReason,
getCallingPackage,
} from '@solana-mobile/mobile-wallet-adapter-walletlib';

import AuthenticationScreen from '../bottomsheets/AuthenticationScreen';
import SignAndSendTransactionsScreen from '../bottomsheets/SignAndSendTransactionsScreen';
import SignPayloadsScreen from '../bottomsheets/SignPayloadsScreen';
import WalletProvider from '../components/WalletProvider';
import WalletProvider, { useWallet } from '../components/WalletProvider';
import ClientTrustProvider from "../components/ClientTrustProvider";
import {
ClientTrustUseCase,
Expand Down Expand Up @@ -53,6 +54,7 @@ function getRequestScreenComponent(request: MWARequest | null | undefined) {

export default function MobileWalletAdapterEntrypointBottomSheet() {
const [isVisible, setIsVisible] = useState(true);
const {wallet} = useWallet();
const [clientTrustUseCase, setClientTrustUseCase] = useState<ClientTrustUseCase | null>(null)
const [curRequest, setCurRequest] = useState<MWARequest | undefined>(
undefined,
Expand Down Expand Up @@ -130,20 +132,28 @@ export default function MobileWalletAdapterEntrypointBottomSheet() {
]).then((verificationState) => {
if (verificationState instanceof VerificationSucceeded) {
console.log('Reauthorization source verification succeeded')
resolve(request, {} as ReauthorizeDappResponse)
resolve(request, {
authorizationScope: new TextEncoder().encode(verificationState?.authorizationScope)
} as ReuthorizeDappCompleteResponse)
} else if (verificationState instanceof NotVerifiable) {
console.log('Reauthorization source not verifiable; approving')
resolve(request, {} as ReauthorizeDappResponse)
resolve(request, {
authorizationScope: new TextEncoder().encode(verificationState?.authorizationScope)
} as ReuthorizeDappCompleteResponse)
} else if (verificationState instanceof VerificationFailed) {
console.log('Reauthorization source verification failed')
resolve(request, {failReason: MWARequestFailReason.UserDeclined})
resolve(request, {failReason: MWARequestFailReason.AuthorizationNotValid})
}
}).catch(() => {
console.log('Timed out waiting for reauthorization source verification')
resolve(request, {failReason: MWARequestFailReason.UserDeclined})
resolve(request, {failReason: MWARequestFailReason.AuthorizationNotValid})
});
}
}, [curRequest, endWalletSession]);

if (curRequest.__type === MWARequestType.DeauthorizeDappRequest) {
resolve(curRequest, {} as DeauthorizedDappResponse)
}
}, [wallet, curRequest, endWalletSession]);

// Start an MWA session
useMobileWalletAdapterSession(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.solanamobile.mobilewalletadapterwalletlib.reactnative

import com.solanamobile.mobilewalletadapterwalletlib.reactnative.model.AuthorizeDappResponse
import com.solanamobile.mobilewalletadapterwalletlib.reactnative.model.DeauthorizeDappResponse
import com.solanamobile.mobilewalletadapterwalletlib.reactnative.model.MobileWalletAdapterFailureResponse
import com.solanamobile.mobilewalletadapterwalletlib.reactnative.model.MobileWalletAdapterRequest
import com.solanamobile.mobilewalletadapterwalletlib.reactnative.model.MobileWalletAdapterResponse
Expand Down Expand Up @@ -76,9 +77,10 @@ internal object MobileWalletAdapterResponseSerializer : JsonContentPolymorphicSe
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out MobileWalletAdapterResponse> =
if ((element as? JsonObject)?.containsKey("failReason") == true) FailReasonTransformingSerializer
else if ((element as? JsonObject)?.containsKey("publicKey") == true) AuthorizeDappResponse.serializer()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we also include a __type in the response object, so instead of checking for unique fields we can just check a more readable __type field. Not a big deal/blocker though and probably not worth a whole refactor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I actually started to add that but ran into some issues on the JS side due to the use of types vs classes. I couldn't find a clean way to enforce the __type field so it was getting omitted if I did not explicitly add it to every response, which seemed like a highly error prone situation if we are relying on those types for deserialization. Obviously there has to be a better way to handle this but decided to leave it out for a separate PR.

else if ((element as? JsonObject)?.containsKey("authorizationScope") == true) ReauthorizeDappResponse.serializer()
else if ((element as? JsonObject)?.containsKey("signedPayloads") == true) SignedPayloads.serializer()
else if ((element as? JsonObject)?.containsKey("signedTransactions") == true) SignedAndSentTransactions.serializer()
else if ((element as? JsonObject)?.isEmpty() == true) ReauthorizeDappResponse.serializer()
else if ((element as? JsonObject)?.isEmpty() == true) DeauthorizeDappResponse.serializer()
else MobileWalletAdapterResponse.serializer()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class SolanaMobileWalletAdapterWalletLibModule(val reactContext: ReactApplicatio
val id: String = UUID.randomUUID().toString()) {
data class AuthorizeDapp(override val request: AuthorizeRequest) : MobileWalletAdapterRemoteRequest(request)
data class ReauthorizeDapp(override val request: ReauthorizeRequest) : MobileWalletAdapterRemoteRequest(request)
data class DeauthorizeDapp(override val request: DeauthorizedEvent) : MobileWalletAdapterRemoteRequest(request)

sealed class SignPayloads(override val request: SignPayloadsRequest) : MobileWalletAdapterRemoteRequest(request)
data class SignTransactions(override val request: SignTransactionsRequest) : SignPayloads(request)
Expand Down Expand Up @@ -196,7 +197,7 @@ class SolanaMobileWalletAdapterWalletLibModule(val reactContext: ReactApplicatio
is ReauthorizeDapp -> when (response) {
is MobileWalletAdapterFailureResponse -> {
when (response) {
is UserDeclinedResponse ->
is AuthorizationNotValidResponse ->
(pendingRequest as? MobileWalletAdapterRemoteRequest.ReauthorizeDapp)?.request?.completeWithDecline()
else -> completeWithInvalidResponse()
}
Expand All @@ -205,6 +206,11 @@ class SolanaMobileWalletAdapterWalletLibModule(val reactContext: ReactApplicatio
(pendingRequest as? MobileWalletAdapterRemoteRequest.ReauthorizeDapp)?.request?.completeWithReauthorize()
else -> completeWithInvalidResponse()
}
is DeauthorizeDapp -> when (response) {
is DeauthorizeDappResponse ->
(pendingRequest as? MobileWalletAdapterRemoteRequest.DeauthorizeDapp)?.request?.complete()
else -> completeWithInvalidResponse()
}
is SignAndSendTransactions -> when (response) {
is MobileWalletAdapterFailureResponse -> {
when (response) {
Expand Down Expand Up @@ -277,6 +283,11 @@ class SolanaMobileWalletAdapterWalletLibModule(val reactContext: ReactApplicatio
request.request.identityUri.toString(), request.request.iconRelativeUri.toString(),
request.request.authorizationScope
)
is MobileWalletAdapterRemoteRequest.DeauthorizeDapp -> DeauthorizeDapp(
scenarioId!!, request.request.cluster, request.request.identityName,
request.request.identityUri.toString(), request.request.iconRelativeUri.toString(),
request.request.authorizationScope
)
is MobileWalletAdapterRemoteRequest.SignMessages -> SignMessages(
scenarioId!!, request.request.cluster, request.request.identityName,
request.request.identityUri.toString(), request.request.iconRelativeUri.toString(),
Expand Down Expand Up @@ -378,7 +389,9 @@ class SolanaMobileWalletAdapterWalletLibModule(val reactContext: ReactApplicatio
}

override fun onDeauthorizedEvent(event: DeauthorizedEvent) {
event.complete()
val request = MobileWalletAdapterRemoteRequest.DeauthorizeDapp(event)
pendingRequests.put(request.id, request)
sendWalletServiceRequestToReact(request)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ data class ReauthorizeDapp(
override val authorizationScope: ByteArray
) : VerifiableIdentityRequestSurrogate()

@Serializable
@SerialName("DEAUTHORIZE_DAPP")
data class DeauthorizeDapp(
override val sessionId: String,
override val cluster: String,
override val identityName: String?,
override val identityUri: String?,
override val iconRelativeUri: String?,
override val authorizationScope: ByteArray
) : VerifiableIdentityRequestSurrogate()

@Serializable
sealed class SignPayloads : VerifiableIdentityRequestSurrogate() {
abstract val payloads: List<ByteArray>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,19 @@ data class AuthorizeDappResponse(
@Serializable(with = ByteArrayAsMapSerializer::class) val publicKey: ByteArray,
val accountLabel: String? = String(publicKey),
val walletUriBase: String? = null,
@Serializable(with = ByteArrayAsMapSerializer::class) val authorizationScope: ByteArray? = null
@Serializable(with = ByteArrayAsMapSerializer::class) val authorizationScope: ByteArray
) : MobileWalletAdapterResponse()

@Serializable
object ReauthorizeDappResponse : MobileWalletAdapterResponse()
data class ReauthorizeDappResponse(
@Serializable(with = ByteArrayAsMapSerializer::class) val publicKey: ByteArray? = null,
val accountLabel: String? = publicKey?.let { String(publicKey) },
val walletUriBase: String? = null,
@Serializable(with = ByteArrayAsMapSerializer::class) val authorizationScope: ByteArray
) : MobileWalletAdapterResponse()

@Serializable
object DeauthorizeDappResponse : MobileWalletAdapterResponse()

@Serializable
data class SignedPayloads(
Expand Down
19 changes: 15 additions & 4 deletions js/packages/mobile-wallet-adapter-walletlib/src/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export type MWARequest =
| SignTransactionsRequest
| SignAndSendTransactionsRequest
| AuthorizeDappRequest
| ReauthorizeDappRequest;
| ReauthorizeDappRequest
| DeauthorizeDappRequest;

export enum MWARequestType {
AuthorizeDappRequest = 'AUTHORIZE_DAPP',
Expand All @@ -50,6 +51,7 @@ export enum MWARequestType {
SignTransactionsRequest = 'SIGN_TRANSACTIONS',
SignAndSendTransactionsRequest = 'SIGN_AND_SEND_TRANSACTIONS',
}

interface IMWARequest {
__type: MWARequestType;
requestId: string;
Expand Down Expand Up @@ -99,6 +101,8 @@ export type SignAndSendTransactionsRequest = Readonly<{

export type MWAResponse =
| AuthorizeDappResponse
| ReauthorizeDappResponse
| DeauthorizeDappResponse
| SignMessagesResponse
| SignTransactionsResponse
| SignAndSendTransactionsResponse;
Expand Down Expand Up @@ -138,8 +142,14 @@ export type AuthorizeDappCompleteResponse = Readonly<{
export type AuthorizeDappResponse = AuthorizeDappCompleteResponse | UserDeclinedResponse;

/* Reauthorize Dapp */
export type ReauthorizeDappCompleteResponse = Readonly<{}>;
export type ReauthorizeDappResponse = ReauthorizeDappCompleteResponse | UserDeclinedResponse;
export type ReauthorizeDappCompleteResponse = Readonly<{
authorizationScope?: Uint8Array;
}>;
export type ReauthorizeDappResponse = ReauthorizeDappCompleteResponse | AuthorizationNotValidResponse;

/* Deauthorize Dapp */
export type DeauthorizeDappCompleteResponse = Readonly<{}>;
export type DeauthorizeDappResponse = DeauthorizeDappCompleteResponse | AuthorizationNotValidResponse;

/* Sign Transactions/Messages */
export type SignPayloadsCompleteResponse = Readonly<{ signedPayloads: Uint8Array[] }>;
Expand All @@ -163,9 +173,10 @@ export type SignAndSendTransactionsResponse =

export function resolve(request: AuthorizeDappRequest, response: AuthorizeDappResponse): void;
export function resolve(request: ReauthorizeDappRequest, response: ReauthorizeDappResponse): void;
export function resolve(request: DeauthorizeDappRequest, response: DeauthorizeDappResponse): void;
export function resolve(request: SignMessagesRequest, response: SignMessagesResponse): void;
export function resolve(request: SignTransactionsRequest, response: SignTransactionsResponse): void;
export function resolve(request: SignAndSendTransactionsRequest, response: SignAndSendTransactionsResponse): void;
export function resolve(request: MWARequest, response: unknown): void {
export function resolve(request: MWARequest, response: MWAResponse): void {
SolanaMobileWalletAdapterWalletLib.resolve(JSON.stringify(request), JSON.stringify(response));
}