Skip to content
Open
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
2 changes: 0 additions & 2 deletions example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,8 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation project(':magic:core')
// implementation project(':magic:ext:oauth')
// implementation project(':magic:ext:oidc')
// implementation 'link.magic:magic-android:[10.4.0,11.0['
implementation 'link.magic:magic-ext-oauth:[5.0,6.0['
implementation 'link.magic:magic-ext-oidc:[2.0,3.0['
implementation "org.web3j:core:$web3j_version"
implementation "org.web3j:geth:$web3j_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
Expand Down
27 changes: 0 additions & 27 deletions example/src/main/java/link/magic/demo/login/MALoginActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import link.magic.android.extension.oauth.oauth
import link.magic.android.extension.oauth.requestConfiguration.OAuthConfiguration
import link.magic.android.extension.oauth.requestConfiguration.OAuthProvider
import link.magic.android.extension.oauth.response.OAuthResponse
import link.magic.android.extension.oidc.openid
import link.magic.android.extension.oidc.requestConfiguration.OpenIdConfiguration
import link.magic.android.modules.auth.requestConfiguration.LoginWithEmailOTPConfiguration
import link.magic.android.modules.auth.requestConfiguration.LoginWithSMSConfiguration
import link.magic.android.modules.auth.requestConfiguration.OverridesConfiguration
Expand Down Expand Up @@ -55,10 +53,6 @@ class MALoginActivity : UtilActivity(), AdapterView.OnItemSelectedListener, Magi
socialLoginButton.setOnClickListener {
loginViaSocialProviders(it)
}
val openIdLoginButton : Button = findViewById<Button>(R.id.openId_login_btn)
openIdLoginButton.setOnClickListener {
loginWithOpenId(it)
}

val mcLoginButton : Button = findViewById<Button>(R.id.mc_login_btn)
mcLoginButton.setOnClickListener {
Expand Down Expand Up @@ -188,27 +182,6 @@ class MALoginActivity : UtilActivity(), AdapterView.OnItemSelectedListener, Magi
super.onActivityResult(requestCode, resultCode, data)
}

/**
* oidc Extension
*/
private fun loginWithOpenId(v: View) {
val providerId = findViewById<EditText>(R.id.provider_id_text)
val jwt = findViewById<EditText>(R.id.jwt_text)
val configuration = OpenIdConfiguration(jwt.text.toString(), providerId.text.toString())
val did = (magic as Magic).openid.loginWithOIDC(this, configuration)
did.whenComplete { response: DIDToken?, error: Throwable? ->
if (error != null) {
Log.d("error", error.localizedMessage)
}
if (response != null && !response.hasError()) {
response.result?.let { Log.d("login", it) }

} else {
Log.d("login", "OpenID Not Logged in")
}
}
}

/**
* Recover Account
*/
Expand Down
29 changes: 1 addition & 28 deletions example/src/main/res/layout/activity_ma_login.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,38 +70,11 @@
android:layout_marginTop="10dp"
android:text="@string/social_login" />

<EditText
android:id="@+id/provider_id_text"
android:layout_width="298dp"
android:layout_height="wrap_content"
android:layout_below="@+id/social_login_btn"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:hint="@string/provider_id_hint"
android:inputType="text" />
<EditText
android:id="@+id/jwt_text"
android:layout_width="298dp"
android:layout_height="wrap_content"
android:layout_below="@+id/provider_id_text"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:hint="@string/jwt_hint"
android:inputType="text" />
<Button
android:id="@+id/openId_login_btn"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_below="@+id/jwt_text"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:text="@string/openid_login" />

<EditText
android:id="@+id/recovery_email_input"
android:layout_width="298dp"
android:layout_height="wrap_content"
android:layout_below="@+id/openId_login_btn"
android:layout_below="@+id/social_login_btn"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:autofillHints="phone"
Expand Down
3 changes: 0 additions & 3 deletions example/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@
<string name="sign_typed_data_legacy_json">Sign Typed Data Legacy Json</string>
<string name="Contract_write">Contract Write</string>
<string name="social_login">Social Login</string>
<string name="openid_login">OpenID Login</string>
<string name="mc_login">Magic Connect Login</string>
<string name="provider_id_hint">Please enter provider Id</string>
<string name="jwt_hint">Please enter JWT</string>
<string name="sms_login">SMS Login</string>

<!-- MC -->
Expand Down
2 changes: 1 addition & 1 deletion magic/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ dependencies {
implementation "com.google.code.gson:gson:$gson_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation 'com.nimbusds:nimbus-jose-jwt:9.31'
implementation 'com.nimbusds:nimbus-jose-jwt:10.10'
}

apply from: 'deploy.gradle'
36 changes: 27 additions & 9 deletions magic/core/src/main/java/link/magic/android/Magic.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ import link.magic.android.core.provider.RpcProvider
import link.magic.android.core.relayer.urlBuilder.URLBuilder
import link.magic.android.core.relayer.urlBuilder.network.CustomNodeConfiguration
import link.magic.android.modules.auth.AuthModule
import link.magic.android.modules.bitcoin.BitcoinConfiguration
import link.magic.android.modules.bitcoin.BitcoinExtension
import link.magic.android.modules.events.EventModule
import link.magic.android.modules.extension.ExtensionConfiguration
import link.magic.android.modules.extension.ExtensionConfigurations
import link.magic.android.modules.solana.SolanaConfiguration
import link.magic.android.modules.solana.SolanaExtension
import link.magic.android.modules.user.UserModule
import link.magic.android.modules.wallet.WalletModule
import link.magic.android.modules.web3j.signTypedData.SignTypedDataExtension
import java.util.*

class Magic private constructor(applicationContext: Context, urlBuilder: URLBuilder) {
class Magic private constructor(applicationContext: Context, urlBuilder: URLBuilder, extensions: List<ExtensionConfiguration>) {

companion object {
var debugEnabled = false
Expand Down Expand Up @@ -52,21 +58,33 @@ class Magic private constructor(applicationContext: Context, urlBuilder: URLBuil
*/
val events = EventModule(rpcProvider)

/**
* Contains Solana methods. `null` unless `Magic` was created with a [SolanaConfiguration].
*/
val solana: SolanaExtension? =
if (extensions.any { it is SolanaConfiguration }) SolanaExtension(rpcProvider) else null

/**
* Contains Bitcoin methods. `null` unless `Magic` was created with a [BitcoinConfiguration].
*/
val bitcoin: BitcoinExtension? =
if (extensions.any { it is BitcoinConfiguration }) BitcoinExtension(rpcProvider) else null

// Default initializer
constructor(ctx:Context, apiKey: String, locale: String = defaultLocale)
: this(ctx, URLBuilder(apiKey, EthNetwork.Mainnet, locale, ctx.packageName, emptyMap()))
constructor(ctx:Context, apiKey: String, locale: String = defaultLocale, extensions: List<ExtensionConfiguration> = emptyList())
: this(ctx, URLBuilder(apiKey, EthNetwork.Mainnet, locale, ctx.packageName, emptyMap(), ExtensionConfigurations.toParams(extensions)), extensions)

// Meta initializer
constructor(ctx:Context, apiKey: String, meta: Map<String, Any>, locale: String = defaultLocale)
: this(ctx, URLBuilder(apiKey, EthNetwork.Mainnet, locale, ctx.packageName, meta))
constructor(ctx:Context, apiKey: String, meta: Map<String, Any>, locale: String = defaultLocale, extensions: List<ExtensionConfiguration> = emptyList())
: this(ctx, URLBuilder(apiKey, EthNetwork.Mainnet, locale, ctx.packageName, meta, ExtensionConfigurations.toParams(extensions)), extensions)

// Eth Network initializer
constructor(ctx:Context, apiKey: String, ethNetwork: EthNetwork, locale: String = defaultLocale)
: this(ctx, URLBuilder(apiKey, ethNetwork, locale, ctx.packageName, emptyMap()))
constructor(ctx:Context, apiKey: String, ethNetwork: EthNetwork, locale: String = defaultLocale, extensions: List<ExtensionConfiguration> = emptyList())
: this(ctx, URLBuilder(apiKey, ethNetwork, locale, ctx.packageName, emptyMap(), ExtensionConfigurations.toParams(extensions)), extensions)

// Custom Node Initializer
constructor(ctx:Context, apiKey: String, customNodeConfiguration: CustomNodeConfiguration, meta: Map<String, Any> = emptyMap(), locale: String = defaultLocale)
: this(ctx, URLBuilder(apiKey, customNodeConfiguration, locale, ctx.packageName, meta))
constructor(ctx:Context, apiKey: String, customNodeConfiguration: CustomNodeConfiguration, meta: Map<String, Any> = emptyMap(), locale: String = defaultLocale, extensions: List<ExtensionConfiguration> = emptyList())
: this(ctx, URLBuilder(apiKey, customNodeConfiguration, locale, ctx.packageName, meta, ExtensionConfigurations.toParams(extensions)), extensions)

// Method to change the activity context when needed
fun setContext(newContext: Context) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package link.magic.android.core.provider

import androidx.annotation.Keep
import link.magic.android.core.relayer.message.DPop
import link.magic.android.core.relayer.message.OutboundMessageType

/**
* A JSON-RPC request whose `params` is a single object, not a list. The Solana
* methods are sent this way, as in magic-js. web3j's `Request` only holds a list,
* so these requests skip it and have their own types.
*/
@Keep
internal class ObjectParamsRequest(
private var method: String,
private var params: Any,
private var id: Long
) {

private val jsonrpc = "2.0"
}

@Keep
internal class MagicObjectParamsRequestData(val payload: ObjectParamsRequest, outboundMessageType: OutboundMessageType, encodedParams: String) {
val msgType: String = "$outboundMessageType-$encodedParams"
val jwt: String = DPop.createJwt()
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,23 +98,43 @@ class RpcProvider internal constructor(initialContext: Context, val urlBuilder:
/* Overwrite id with random Long number */
val newRandomId = Number().generateRandomId()

val result = CompletableFuture<T>()
val rpcPayload = RequestForSerialization(requestPayload.method, requestPayload.params, newRandomId)
val request = MagicRequestData(rpcPayload, OutboundMessageType.MAGIC_HANDLE_REQUEST, urlBuilder.encodedParams);

// Serialize class to Json Object
val message = GsonExtension()
.serialize()
.toJson(request)
.replace("\\n", "")

return enqueue(message, newRandomId, responseType)
}

/**
* Send a request whose `params` is a single object, not a list, to the webview.
*/
internal fun <T : Response<*>> sendAsync(method: String, params: Any, responseType: Class<T>): CompletableFuture<T> {
val newRandomId = Number().generateRandomId()

val rpcPayload = ObjectParamsRequest(method, params, newRandomId)
val request = MagicObjectParamsRequestData(rpcPayload, OutboundMessageType.MAGIC_HANDLE_REQUEST, urlBuilder.encodedParams)

val message = GsonExtension()
.serialize()
.toJson(request)

return enqueue(message, newRandomId, responseType)
}

private fun <T : Response<*>> enqueue(message: String, id: Long, responseType: Class<T>): CompletableFuture<T> {
val result = CompletableFuture<T>()

if (Magic.debugEnabled) {
Log.d("Magic", "Prepare Message: $message")
}

/* send the promise to webview queue waiting for dispatch.
When the payload result comes back the handler will resolve the result asynchronously */
overlay.enqueue(message, newRandomId, fun (responseString: String) {
overlay.enqueue(message, id, fun (responseString: String) {
val data = Gson().fromJson(responseString, responseType)
result.complete(data)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ open class BaseOptions(
val host: String,
val locale: String,
val bundleId: String,
val meta: Map<String, Any>
val meta: Map<String, Any>,
val ext: Map<String, Any>? = null
): Serializable {
val sdk = "magic-sdk-android"
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ class URLBuilder private constructor(var options: BaseOptions, val apiKey: Strin
val encodedParams: String
get() {
val paramsInBytes = gson.toJson(options).toByteArray()
return Base64.encodeToString(paramsInBytes, Base64.DEFAULT)
return Base64.encodeToString(paramsInBytes, Base64.NO_WRAP)
}

companion object {
private const val mgboxHost = "https://box.magic.link"
}

internal constructor(apiKey: String, customNode: CustomNodeConfiguration, locale: String, bundleId: String, meta: Map<String, Any>)
: this(CustomNodeBaseOptions(apiKey, customNode, mgboxHost, locale, bundleId, meta), apiKey)
internal constructor(apiKey: String, network: EthNetwork, locale: String, bundleId: String, meta: Map<String, Any>)
: this(NetworkBaseOptions(apiKey, network.toString().lowercase(), mgboxHost, locale, bundleId, meta), apiKey)
internal constructor(apiKey: String, customNode: CustomNodeConfiguration, locale: String, bundleId: String, meta: Map<String, Any>, ext: Map<String, Any>? = null)
: this(CustomNodeBaseOptions(apiKey, customNode, mgboxHost, locale, bundleId, meta, ext), apiKey)
internal constructor(apiKey: String, network: EthNetwork, locale: String, bundleId: String, meta: Map<String, Any>, ext: Map<String, Any>? = null)
: this(NetworkBaseOptions(apiKey, network.toString().lowercase(), mgboxHost, locale, bundleId, meta, ext), apiKey)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ internal class CustomNodeBaseOptions internal constructor(
mgboxHost: String,
locale: String,
bundleId: String,
meta: Map<String, Any>
) : BaseOptions(apiKey, mgboxHost, locale, bundleId, meta)
meta: Map<String, Any>,
ext: Map<String, Any>? = null
) : BaseOptions(apiKey, mgboxHost, locale, bundleId, meta, ext)
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ internal class NetworkBaseOptions(
mgboxHost: String,
locale: String,
bundleId: String,
meta: Map<String, Any>
) : BaseOptions(apiKey, mgboxHost, locale, bundleId, meta)
meta: Map<String, Any>,
ext: Map<String, Any>? = null
) : BaseOptions(apiKey, mgboxHost, locale, bundleId, meta, ext)
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package link.magic.android.modules.bitcoin

import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import link.magic.android.modules.extension.ExtensionConfiguration
import org.web3j.protocol.core.Response

/**
* Enables the Bitcoin extension. Mirrors `BitcoinConfig` in `@magic-ext/bitcoin`.
*
* @param rpcUrl The Bitcoin node or indexer URL.
* @param network The network name, e.g. `mainnet` or `testnet`.
*/
@Keep
class BitcoinConfiguration(val rpcUrl: String, network: String) : ExtensionConfiguration {
override val name: String
get() = "bitcoin"

val chainType: String = BitcoinExtension.CHAIN_TYPE

val options = Options(network)

@Keep
class Options(val network: String)
}

/**
* A UTXO to spend. [value] is in BTC (not satoshis), with at most eight decimal
* places. The enclave needs this value to compute its segwit (BIP143) sighash.
* [txNum] is the output index (vout) of the UTXO within its source transaction.
*/
@Keep
class BitcoinTransactionInput(
val txid: String,
@SerializedName("tx_num") val txNum: Int,
val value: Double,
val address: String? = null
)

/**
* A transaction output. [value] is in BTC with at most eight decimal places.
*/
@Keep
class BitcoinTransactionOutput(
val address: String,
val value: Double
)

/**
* The result of `signTransaction`.
*/
@Keep
class BitcoinSignedTransaction(
/** The broadcast-ready, fully-signed transaction as hex. */
val signedTransaction: String,
/** The transaction hash (txid). */
val transactionHash: String
)

@Keep
class BitcoinSignTransactionResponse : Response<BitcoinSignedTransaction>()

/**
* Params for `btc_signTransaction`.
*/
@Keep
internal class BitcoinTransactionParams(
val inputs: List<BitcoinTransactionInput>,
val outputs: List<BitcoinTransactionOutput>
)
Loading
Loading