squash
fix android module name import fixes fix some imports use `@atproto/jwk` over local zod schema preview oauth client rename package alignment more alignment database implementation cleanup some types alignment switch implementation for es256 half of `verifyJwt` for android half of `verifyJwt` for iOS rm log `createJwt` android `createJwt` ios rm print cleanup native impl silence some warnings reorg, finish web impl follow the `CryptoImplementation` move some more things around normalize some names rename func add generate on android add android dep rm bool add alg better implementation ios jwk generation fix index.ts add `getRandomValues()` on native web `getRandomValues` silence ts error for missing crypto on web add `digest` for web add `digest` add module
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
group = 'expo.modules.blueskyoauthclient'
|
||||
version = '0.0.1'
|
||||
|
||||
buildscript {
|
||||
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
||||
if (expoModulesCorePlugin.exists()) {
|
||||
apply from: expoModulesCorePlugin
|
||||
applyKotlinExpoModulesCorePlugin()
|
||||
}
|
||||
|
||||
// Simple helper that allows the root project to override versions declared by this library.
|
||||
ext.safeExtGet = { prop, fallback ->
|
||||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
}
|
||||
|
||||
// Ensures backward compatibility
|
||||
ext.getKotlinVersion = {
|
||||
if (ext.has("kotlinVersion")) {
|
||||
ext.kotlinVersion()
|
||||
} else {
|
||||
ext.safeExtGet("kotlinVersion", "1.8.10")
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications {
|
||||
release(MavenPublication) {
|
||||
from components.release
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url = mavenLocal().url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion safeExtGet("compileSdkVersion", 33)
|
||||
|
||||
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
|
||||
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
||||
}
|
||||
}
|
||||
|
||||
namespace "expo.modules.blueskyoauthclient"
|
||||
defaultConfig {
|
||||
minSdkVersion safeExtGet("minSdkVersion", 21)
|
||||
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
||||
versionCode 1
|
||||
versionName "0.0.1"
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
publishing {
|
||||
singleVariant("release") {
|
||||
withSourcesJar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':expo-modules-core')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
|
||||
implementation "com.nimbusds:nimbus-jose-jwt:9.38-rc3"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<manifest>
|
||||
</manifest>
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package expo.modules.blueskyoauthclient
|
||||
|
||||
import com.nimbusds.jose.Algorithm
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.MessageDigest
|
||||
import java.security.interfaces.ECPublicKey
|
||||
import java.security.interfaces.ECPrivateKey
|
||||
import com.nimbusds.jose.jwk.Curve
|
||||
import com.nimbusds.jose.jwk.ECKey
|
||||
import com.nimbusds.jose.jwk.KeyUse
|
||||
import java.util.UUID
|
||||
|
||||
class CryptoUtil {
|
||||
fun digest(data: ByteArray): ByteArray {
|
||||
val digest = MessageDigest.getInstance("sha256")
|
||||
return digest.digest(data)
|
||||
}
|
||||
|
||||
fun getRandomValues(byteLength: Int): ByteArray {
|
||||
val random = ByteArray(byteLength)
|
||||
java.security.SecureRandom().nextBytes(random)
|
||||
return random
|
||||
}
|
||||
|
||||
fun generateKeyPair(keyId: String?): Pair<String, String> {
|
||||
val keyIdString = keyId ?: UUID.randomUUID().toString()
|
||||
|
||||
val keyPairGen = KeyPairGenerator.getInstance("EC")
|
||||
keyPairGen.initialize(Curve.P_256.toECParameterSpec())
|
||||
val keyPair = keyPairGen.generateKeyPair()
|
||||
|
||||
val publicKey = keyPair.public as ECPublicKey
|
||||
val privateKey = keyPair.private as ECPrivateKey
|
||||
|
||||
val publicJwk = ECKey.Builder(Curve.P_256, publicKey)
|
||||
.keyUse(KeyUse.SIGNATURE)
|
||||
.algorithm(Algorithm.parse("ES256"))
|
||||
.keyID(keyIdString)
|
||||
.build()
|
||||
val privateJwk = ECKey.Builder(Curve.P_256, publicKey)
|
||||
.privateKey(privateKey)
|
||||
.keyUse(KeyUse.SIGNATURE)
|
||||
.keyID(keyIdString)
|
||||
.algorithm(Algorithm.parse("ES256"))
|
||||
.build()
|
||||
|
||||
return Pair(
|
||||
publicJwk.toString(),
|
||||
privateJwk.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package expo.modules.blueskyoauthclient
|
||||
|
||||
import expo.modules.kotlin.modules.Module
|
||||
import expo.modules.kotlin.modules.ModuleDefinition
|
||||
|
||||
class ExpoBlueskyOAuthClientModule : Module() {
|
||||
override fun definition() = ModuleDefinition {
|
||||
Name("ExpoBlueskyOAuthClient")
|
||||
|
||||
AsyncFunction("digest") { value: ByteArray ->
|
||||
return@AsyncFunction CryptoUtil().digest(value)
|
||||
}
|
||||
|
||||
Function("getRandomValues") { byteLength: Int ->
|
||||
return@Function CryptoUtil().getRandomValues(byteLength)
|
||||
}
|
||||
|
||||
AsyncFunction("generateKeyPair") { keyId: String? ->
|
||||
val res = CryptoUtil().generateKeyPair(keyId)
|
||||
|
||||
return@AsyncFunction mapOf(
|
||||
"publicKey" to res.first,
|
||||
"privateKey" to res.second
|
||||
)
|
||||
}
|
||||
|
||||
AsyncFunction("createJwt") { jwkString: String, headerString: String, payloadString: String ->
|
||||
return@AsyncFunction JWTUtil().createJwt(jwkString, headerString, payloadString)
|
||||
}
|
||||
|
||||
AsyncFunction("verifyJwt") { jwkString: String, tokenString: String, options: String? ->
|
||||
return@AsyncFunction JWTUtil().verifyJwt(jwkString, tokenString, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package expo.modules.blueskyoauthclient
|
||||
|
||||
import com.nimbusds.jose.JWSHeader
|
||||
import com.nimbusds.jose.crypto.ECDSASigner
|
||||
import com.nimbusds.jose.crypto.ECDSAVerifier
|
||||
import com.nimbusds.jose.jwk.ECKey
|
||||
import com.nimbusds.jwt.JWTClaimsSet
|
||||
import com.nimbusds.jwt.SignedJWT
|
||||
|
||||
|
||||
class JWTUtil {
|
||||
fun createJwt(jwkString: String, headerString: String, payloadString: String): String {
|
||||
val key = ECKey.parse(jwkString)
|
||||
val header = JWSHeader.parse(headerString)
|
||||
val payload = JWTClaimsSet.parse(payloadString)
|
||||
|
||||
val signer = ECDSASigner(key)
|
||||
val jwt = SignedJWT(header, payload)
|
||||
jwt.sign(signer)
|
||||
|
||||
return jwt.serialize()
|
||||
}
|
||||
|
||||
fun verifyJwt(jwkString: String, tokenString: String, options: String?): Boolean {
|
||||
return try {
|
||||
val key = ECKey.parse(jwkString)
|
||||
val jwt = SignedJWT.parse(tokenString)
|
||||
val verifier = ECDSAVerifier(key)
|
||||
|
||||
jwt.verify(verifier)
|
||||
} catch(e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user