swift impl
This commit is contained in:
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})()
|
||||
}, [])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user