swift impl

This commit is contained in:
Hailey
2024-04-15 00:37:12 -07:00
parent 2ada0cbd0d
commit 2c1b3709af
7 changed files with 62 additions and 28 deletions
@@ -38,8 +38,8 @@ public class ExpoBlueskyOAuthClientModule: Module {
promise.resolve(jwt)
}
AsyncFunction("verifyJwt") { (jwk: String, token: String, options: String?, promise: Promise) in
promise.resolve(JWTUtil.verifyJwt(jwk, token: token, options: options))
AsyncFunction("verifyJwt") { (token: String, jwk: JWK, promise: Promise) in
promise.resolve(JWTUtil.verifyJwt(token: token, jwk: jwk))
}
}
}
@@ -27,7 +27,7 @@ struct JWK : Record {
return Field(wrappedValue: self)
}
func toSecKey() throws -> SecKey? {
func toPrivateSecKey() throws -> SecKey? {
let jsonData = try JSONSerialization.data(withJSONObject: self.toDictionary())
guard let jwk = try? JSONDecoder().decode(ECPrivateKey.self, from: jsonData),
let key = try? jwk.converted(to: SecKey.self)
@@ -37,6 +37,17 @@ struct JWK : Record {
}
return key
}
func toPublicSecKey() throws -> SecKey? {
let jsonData = try JSONSerialization.data(withJSONObject: self.toDictionary())
guard let jwk = try? JSONDecoder().decode(ECPublicKey.self, from: jsonData),
let key = try? jwk.converted(to: SecKey.self)
else {
print("Error creating SecKey.")
return nil
}
return key
}
}
struct JWKPair : Record {
@@ -7,14 +7,10 @@ struct JWTHeader : Record {
@Field
var jku: String?
@Field
var jwk: JWK
@Field
var kid: String?
@Field
var x5u: String?
@Field
var x5c: String?
@Field
var x5t: String?
@Field
var typ: String?
@Field
var cty: String?
@@ -179,3 +175,10 @@ struct JWTPayloadAuthorizationDetails : Record {
return Field(wrappedValue: self)
}
}
struct JWTVerifyResponse : Record {
@Field
var protectedHeader: JWTHeader
@Field
var payload: String
}
@@ -1,3 +1,4 @@
import ExpoModulesCore
import JOSESwift
class JWTUtil {
@@ -34,7 +35,7 @@ class JWTUtil {
public static func createJwt(header: JWTHeader, payload: JWTPayload, jwk: JWK) -> String? {
guard let header = try? header.toJWSHeader(),
let payload = try? payload.toPayload(),
let key = try? jwk.toSecKey()
let key = try? jwk.toPrivateSecKey()
else {
print("didn't have one")
return nil
@@ -52,15 +53,34 @@ class JWTUtil {
return jws.compactSerializedString
}
public static func verifyJwt(_ jwkString: String, token tokenString: String, options optionsString: String?) -> Bool {
guard let key = try? jsonToPublicKey(jwkString),
let jws = try? JWS(compactSerialization: tokenString),
public static func verifyJwt(token: String, jwk: JWK) -> JWTVerifyResponse? {
guard let key = try? jwk.toPublicSecKey(),
let jws = try? JWS(compactSerialization: token),
let verifier = Verifier(verifyingAlgorithm: .ES256, key: key),
let isVerified = try? jws.validate(using: verifier).isValid(for: verifier)
let validation = try? jws.validate(using: verifier)
else {
return false
return nil
}
return isVerified
let header = validation.header
let serializedHeader = JWTHeader(
alg: "ES256",
jku: Field(wrappedValue: header.jku?.absoluteString),
kid: Field(wrappedValue:header.kid),
typ: Field(wrappedValue: header.typ),
cty: Field(wrappedValue: header.cty),
crit: Field(wrappedValue: header.cty)
)
let payload = String(data: validation.payload.data(), encoding: .utf8)
guard let payload = payload else {
return nil
}
return JWTVerifyResponse(
protectedHeader: serializedHeader.toField(),
payload: payload.toField()
)
}
}
@@ -40,7 +40,7 @@ export const OauthClientReactNative = (NativeModule as null) || {
_token: Jwt,
_jwk: Jwk,
): Promise<{
payload: Record<string, unknown>
payload: string // this is a JSON response to make Swift a bit easier to work with
protectedHeader: Record<string, unknown>
}> {
throw new Error(LINKING_ERROR)
@@ -2,7 +2,6 @@ import {
jwkValidator,
Jwt,
JwtHeader,
jwtHeaderSchema,
JwtPayload,
jwtPayloadSchema,
Key,
@@ -45,8 +44,13 @@ export class ReactNativeKey extends Key {
>(token: Jwt, options?: VerifyOptions<C>): Promise<VerifyResult<P, C>> {
const result = await OauthClientReactNative.verifyJwt(token, this.jwk)
const payload = jwtPayloadSchema.parse(result.payload)
const protectedHeader = jwtHeaderSchema.parse(result.protectedHeader)
let payloadParsed = JSON.parse(result.payload)
payloadParsed = Object.fromEntries(
Object.entries(payloadParsed as object).filter(([_, v]) => v !== null),
)
const payload = jwtPayloadSchema.parse(payloadParsed)
const protectedHeader = result.protectedHeader
if (options?.audience != null) {
const audience = Array.isArray(options.audience)
@@ -85,6 +89,8 @@ export class ReactNativeKey extends Key {
}
}
console.log(payload)
if (payload.iat == null) {
throw new Error('Missing issued at')
}
+2 -8
View File
@@ -64,17 +64,11 @@ function HomeScreenReady({
},
{
sub: 'test',
iat: Math.floor(Date.now() / 1000),
},
)
console.log(jwt)
// console.log('public', key.publicJwk)
// const jwt = await key.createJwt(
// {alg: 'ES256', kid: key.kid},
// {sub: 'test'},
// )
// console.log(jwt)
const verified = await key.verifyJwt(jwt)
})()
}, [])