Skip to content

Commit

Permalink
✨ webauthn: create expo module
Browse files Browse the repository at this point in the history
  • Loading branch information
cruzdanilo committed Nov 13, 2023
1 parent 5ecd1ef commit c982159
Show file tree
Hide file tree
Showing 19 changed files with 241 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = {
"no-console": "error",
"no-shadow": "off", // @typescript-eslint/no-shadow
},
ignorePatterns: [".expo/types/**/*.ts", "expo-env.d.ts"],
ignorePatterns: ["build/", "dist/", ".expo/types/**/*.ts", "expo-env.d.ts"],
overrides: [
{
files: nodeFiles,
Expand Down
1 change: 1 addition & 0 deletions app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "expo-webauthn";
import "react-native-get-random-values";

import FontAwesome from "@expo/vector-icons/FontAwesome";
Expand Down
4 changes: 2 additions & 2 deletions app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TurnkeyClient } from "@turnkey/http";
import { WebauthnStamper } from "@turnkey/webauthn-stamper";
import React, { useCallback } from "react";
import { Button } from "react-native";
import { Button, Platform } from "react-native";
import * as Sentry from "sentry-expo";
import { Text, XStack, YStack } from "tamagui";
import { useBlockNumber, usePublicClient } from "wagmi";
Expand All @@ -13,7 +13,7 @@ export default function Home() {
const onPress = useCallback(() => {
const organizationId = process.env.EXPO_PUBLIC_TURNKEY_ORGANIZATION_ID;
if (!organizationId) throw new Error("missing EXPO_PUBLIC_TURNKEY_ORGANIZATION_ID");
const rpId = __DEV__ ? "localhost" : "exactly.app";
const rpId = __DEV__ && Platform.OS === "web" ? "localhost" : "exactly.app";
const client = new TurnkeyClient({ baseUrl: "https://api.turnkey.com" }, new WebauthnStamper({ rpId }));

const challenge = generateRandomBuffer();
Expand Down
Binary file modified bun.lockb
Binary file not shown.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
"test:ts:node": "tsc -p tsconfig.node.json",
"test:eslint": "eslint --max-warnings=0 ."
},
"workspaces": [
"webauthn"
],
"engines": {
"node": ">=20.0.0"
},
Expand All @@ -36,6 +39,7 @@
"expo-status-bar": "~1.6.0",
"expo-system-ui": "~2.4.0",
"expo-web-browser": "~12.3.2",
"expo-webauthn": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-native": "^0.72.6",
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"noUncheckedIndexedAccess": true
},
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"],
"exclude": ["**/node_modules", ".tamagui", "*.config.ts"]
"exclude": ["**/node_modules", "**/build", "**/dist", ".tamagui", "*.config.ts"]
}
Empty file added webauthn/.eslintrc.js
Empty file.
11 changes: 11 additions & 0 deletions webauthn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# expo-webauthn

webauthn polyfill for expo

## usage

```ts
import "expo-webauthn";

// use navigator.credentials (https://www.w3.org/TR/webauthn-3/)
```
89 changes: 89 additions & 0 deletions webauthn/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
apply plugin: "com.android.library"
apply plugin: "kotlin-android"
apply plugin: "maven-publish"

group = "expo.modules.webauthn"
version = "0.1.0"

buildscript {
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
if (expoModulesCorePlugin.exists()) {
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
}

ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

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", 34)

compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.majorVersion
}

namespace "expo.modules.webauthn"
defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 21)
targetSdkVersion safeExtGet("targetSdkVersion", 34)
versionCode 1
versionName "0.1.0"
}
lintOptions {
abortOnError false
}
publishing {
singleVariant("release") {
withSourcesJar()
}
}
}

repositories {
mavenCentral()
}

dependencies {
implementation project(":expo-modules-core")
implementation "androidx.credentials:credentials:1.2.0"
implementation "androidx.credentials:credentials-play-services-auth:1.2.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
}
2 changes: 2 additions & 0 deletions webauthn/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<manifest package="expo.modules.webauthn" xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package expo.modules.webauthn

import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition

class ExpoWebauthn : Module() {
override fun definition() = ModuleDefinition {
Name("ExpoWebauthn")
}
}
9 changes: 9 additions & 0 deletions webauthn/expo-module.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"platforms": ["android", "ios", "web"],
"android": {
"modules": ["expo.modules.webauthn.ExpoWebauthn"]
},
"ios": {
"modules": ["ExpoWebauthn"]
}
}
26 changes: 26 additions & 0 deletions webauthn/ios/ExpoWebauthn.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'json'

package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))

Pod::Spec.new do |s|
s.name = 'ExpoWebauthn'
s.version = package['version']
s.summary = package['description']
s.description = package['description']
s.license = package['license']
s.author = package['author']
s.homepage = package['repository']['url']
s.source = { git: package['repository']['url'] }
s.platform = :ios, '13.0'
s.swift_version = '5.4'
s.static_framework = true

s.dependency 'ExpoModulesCore'

s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
'SWIFT_COMPILATION_MODE' => 'wholemodule'
}

s.source_files = "**/*.{h,m,swift}"
end
7 changes: 7 additions & 0 deletions webauthn/ios/ExpoWebauthn.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import ExpoModulesCore

public class ExpoWebauthn: Module {
public func definition() -> ModuleDefinition {
Name("ExpoWebauthn")
}
}
39 changes: 39 additions & 0 deletions webauthn/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "expo-webauthn",
"version": "0.1.0",
"license": "MIT",
"description": "webauthn polyfill for expo",
"author": "danilo neves cruz <[email protected]>",
"repository": {
"type": "git",
"url": "https://github.com/exactly/mobile.git",
"directory": "webauthn"
},
"scripts": {
"build": "expo-module build",
"clean": "expo-module clean",
"lint": "expo-module lint",
"test": "expo-module test",
"prepare": "expo-module prepare",
"prepublishOnly": "expo-module prepublishOnly"
},
"workspaces": [
"example"
],
"dependencies": {
"base64-arraybuffer": "^1.0.2",
"expo-modules-core": "^1.5.11"
},
"devDependencies": {
"expo-module-scripts": "^3.1.0",
"prettier": "^3.1.0",
"typescript": "^5.2.2"
},
"peerDependencies": {
"expo": "*",
"react": "*",
"react-native": "*"
},
"main": "dist/index.js",
"types": "dist/index.d.ts"
}
6 changes: 6 additions & 0 deletions webauthn/src/ExpoWebauthn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { requireNativeModule } from "expo-modules-core";

export default requireNativeModule<{
create: (requestJSON: string) => Promise<string>;
get(requestJSON: string): Promise<string>;
}>("ExpoWebauthn");
1 change: 1 addition & 0 deletions webauthn/src/ExpoWebauthn.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default null;
24 changes: 24 additions & 0 deletions webauthn/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { encode } from "base64-arraybuffer";

import ExpoWebauthn from "./ExpoWebauthn";

// @ts-expect-error -- polyfill
global.navigator.credentials ??= {
get(options) {
if (!options?.publicKey) throw new Error("publicKey required");
return ExpoWebauthn.get(stringify(options.publicKey));
},
async create(options) {
if (!options?.publicKey) throw new Error("publicKey required");
return ExpoWebauthn.create(stringify(options.publicKey));
},
} as CredentialsContainer;

function stringify(value: unknown) {
return JSON.stringify(value, (_, v) => {
if (v instanceof ArrayBuffer) {
return encode(v).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
return v as unknown;
});
}
8 changes: 8 additions & 0 deletions webauthn/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "expo-module-scripts/tsconfig.base",
"compilerOptions": {
"types": ["node"],
"outDir": "dist"
},
"include": ["src"]
}

0 comments on commit c982159

Please sign in to comment.