Skip to content
Merged
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
3 changes: 0 additions & 3 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ repository: https://github.com/corbado/flutter-passkeys
packages:
- packages/**

command:
bootstrap:
usePubspecOverrides: true

scripts:
format:
Expand Down
2 changes: 1 addition & 1 deletion packages/corbado_auth/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dependencies:
sdk: flutter
flutter_secure_storage: "<=9.0.0 >8.0.0"
http: ^1.1.2
json_annotation: ^4.8.1
json_annotation: ^4.10.0
jwt_decoder: ^2.0.1
meta: ^1.15.0
passkeys: ^2.21.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
1F5231B1C0B4B24BD6457DFB /* [CP] Embed Pods Frameworks */,
635C969084CC2623F9C369FA /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -235,6 +236,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
635C969084CC2623F9C369FA /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
Expand Down Expand Up @@ -347,7 +365,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -430,7 +448,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -479,7 +497,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
Expand All @@ -43,6 +44,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
2 changes: 2 additions & 0 deletions packages/passkeys/passkeys/lib/authenticator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class PasskeyAuthenticator implements PasskeyAuthenticatorInterface {
/// Creates a new passkey and stores it on the device.
/// Returns [RegisterResponseType] which must be sent to the relying party
/// server.
@override
Future<RegisterResponseType> register(RegisterRequestType request) async {
if (debugMode) {
await _doctor.check(request.relyingParty.id);
Expand Down Expand Up @@ -102,6 +103,7 @@ class PasskeyAuthenticator implements PasskeyAuthenticatorInterface {
/// Authenticates a user with a passkey.
/// Returns [AuthenticateResponseType] which must be sent to the relying party
/// server.
@override
Future<AuthenticateResponseType> authenticate(
AuthenticateRequestType request,
) async {
Expand Down
4 changes: 4 additions & 0 deletions packages/passkeys/passkeys/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ dev_dependencies:
mocktail: ^1.0.0
plugin_platform_interface: ^2.0.0
very_good_analysis: ^5.0.0

dependency_overrides:
passkeys_platform_interface:
path: ../passkeys_platform_interface
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -89,7 +91,9 @@ public void register(
@Nullable Long timeout,
@Nullable String attestation,
@NonNull List<Messages.ExcludeCredential> excludeCredentials,
@NonNull Messages.Result<Messages.RegisterResponse> result) {
@Nullable String salt,
@NonNull Messages.Result<Messages.RegisterResponse> result
) {
if (android.os.Build.VERSION.SDK_INT < 28) {
result.error(new Messages.FlutterError("android-passkey-unsupported",
"Passkeys are only supported on Android API 28 and above.", null));
Expand Down Expand Up @@ -123,7 +127,21 @@ public void register(
excludeCredentialsType);

try {
String options = createCredentialOptions.toJSON().toString();
JSONObject optionsJson = createCredentialOptions.toJSON();

if (salt != null && !salt.isEmpty()) {
JSONObject extensions = optionsJson.optJSONObject("extensions");
if (extensions == null) extensions = new JSONObject();

JSONObject prf = new JSONObject();
JSONObject eval = new JSONObject();
eval.put("first", salt);
prf.put("eval", eval);

extensions.put("prf", prf);
optionsJson.put("extensions", extensions);
}
String options = optionsJson.toString();

Activity activity = plugin.requireActivity();
CredentialManager credentialManager = CredentialManager.create(activity);
Expand Down Expand Up @@ -158,12 +176,14 @@ public void onResult(CreateCredentialResponse res) {
typedTransports.add("");
}

Map<String, Object> extMap = parsePrfExtensionResults(json);
result.success(new Messages.RegisterResponse.Builder()
.setId(json.getString("id"))
.setRawId(json.getString("rawId"))
.setClientDataJSON(response.getString("clientDataJSON"))
.setAttestationObject(response.getString("attestationObject"))
.setTransports(typedTransports)
.setClientExtensionResults(extMap)
.build());
} catch (JSONException e) {
Log.e(TAG, "Error parsing response: " + resp, e);
Expand Down Expand Up @@ -226,6 +246,7 @@ public void onError(CreateCredentialException e) {
public void authenticate(@NonNull String relyingPartyId, @NonNull String challenge, @Nullable Long timeout,
@Nullable String userVerification, @Nullable List<Messages.AllowCredential> allowCredentials,
@Nullable Boolean preferImmediatelyAvailableCredentials,
@Nullable String salt,
@NonNull Messages.Result<Messages.AuthenticateResponse> result) {
if (android.os.Build.VERSION.SDK_INT < 28) {
result.error(new Messages.FlutterError("android-passkey-unsupported",
Expand All @@ -242,8 +263,20 @@ public void authenticate(@NonNull String relyingPartyId, @NonNull String challen
GetCredentialOptions getCredentialOptions = new GetCredentialOptions(challenge, timeout, relyingPartyId,
allowCredentialsType, userVerification);
try {
String options = getCredentialOptions.toJSON().toString();

JSONObject optionsJson = getCredentialOptions.toJSON();
if (salt != null && !salt.isEmpty()) {
JSONObject extensions = optionsJson.optJSONObject("extensions");
if (extensions == null) extensions = new JSONObject();

JSONObject prf = new JSONObject();
JSONObject eval = new JSONObject();
eval.put("first", salt);
prf.put("eval", eval);

extensions.put("prf", prf);
optionsJson.put("extensions", extensions);
}
String options = optionsJson.toString();
Activity activity = plugin.requireActivity();

CredentialManager credentialManager = CredentialManager.create(activity);
Expand Down Expand Up @@ -282,9 +315,12 @@ public void onResult(GetCredentialResponse res) {
final String signature = response.getString("signature");
final String authenticatorData = response.getString("authenticatorData");

Map<String, Object> extMap = parsePrfExtensionResults(json);
final Messages.AuthenticateResponse msg = new Messages.AuthenticateResponse.Builder()
.setId(id).setRawId(rawId).setClientDataJSON(clientDataJSON)
.setAuthenticatorData(authenticatorData).setSignature(signature)
.setAuthenticatorData(authenticatorData)
.setSignature(signature)
.setClientExtensionResults(extMap)
.setUserHandle(userHandle).build();

result.success(msg);
Expand Down Expand Up @@ -346,4 +382,33 @@ public void cancelCurrentAuthenticatorOperation(@NonNull Messages.Result<Void> r

result.success(null);
}

private static Map<String, Object> parsePrfExtensionResults(JSONObject json) {
JSONObject ext = json.optJSONObject("clientExtensionResults");
if (ext == null) {
return null;
}
JSONObject prf = ext.optJSONObject("prf");
if (prf == null) {
return null;
}

Map<String, Object> prfMap = new HashMap<>();
if (prf.has("enabled")) {
prfMap.put("enabled", prf.optBoolean("enabled"));
}
JSONObject results = prf.optJSONObject("results");
if (results != null && results.has("first")) {
Map<String, Object> resultsMap = new HashMap<>();
resultsMap.put("first", results.optString("first"));
prfMap.put("results", resultsMap);
}
if (prfMap.isEmpty()) {
return null;
}

Map<String, Object> extMap = new HashMap<>();
extMap.put("prf", prfMap);
return extMap;
}
}
Loading
Loading