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
36 changes: 36 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: en-US

reviews:
request_changes_workflow: false
high_level_summary: true
poem: false

path_instructions:
- path: "lib/**/*.dart"
instructions: |
Flag any hardcoded occurrence of the literal `thunderid`/`ThunderID`/`THUNDERID` (any casing) used to
build a runtime key or name — platform-channel storage keys, log tags, and similar.

Do not flag entry point file/package names (e.g. `thunderid_flutter.dart`, `thunderid_client.dart`,
`thunderid_provider.dart`) or types whose purpose IS to represent the SDK itself (e.g.
`ThunderIDClient`, `ThunderIDProvider`). That's a fixed identity, not a per-tenant value.

For everything else, ask: **"Should this read from `ThunderIDConfig.vendor` instead of hardcoding the
vendor name?"** The best fix is avoiding the vendor name entirely when the brand prefix isn't
load-bearing. When a brand-scoped namespace is genuinely required, it should resolve through
`config.vendor` (which already defaults to `'thunderid'`) rather than a literal string. If the same
default-resolution logic starts appearing in more than one place, ask for it to be extracted into a
shared helper instead of repeated inline.

auto_review:
enabled: true
ignore_title_keywords:
- "WIP"
- "DO NOT MERGE"
base_branches:
- main

path_filters:
- "!**/build/**"
- "!**/.dart_tool/**"
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@

Flutter plugin providing the ThunderID authentication SDK (`thunderid_flutter`). Bridges to native iOS (`ThunderID` Swift SDK) and Android (`dev.thunderid:android`) via a `MethodChannel`. The `samples/quickstart` directory contains a standalone demo app.

## Vendor naming rules

The SDK is white-labelable: a consuming app can override the brand/vendor namespace via `ThunderIDConfig.vendor`, so storage keys, log tags, and similar runtime names shouldn't be pinned to one brand.

- Do not hardcode the literal `thunderid`/`ThunderID`/`THUNDERID` (any casing) when building a runtime key/name that a consumer's `vendor` override should control — platform-channel storage keys, log tags, and the like.
- It's fine for **entry point file/package names** (e.g. `thunderid_flutter.dart`, `thunderid_client.dart`, `thunderid_provider.dart`) and types whose purpose IS to represent the SDK itself (e.g. `ThunderIDClient`, `ThunderIDProvider`) to carry the name. That's a fixed identity, not a per-tenant value. Don't flag those.
- Avoiding the vendor name entirely is the best outcome, when the brand prefix isn't actually load-bearing.
- When a brand-scoped namespace is genuinely required, resolve it from `config.vendor` (which already defaults to `'thunderid'`) instead of hardcoding a literal. If the same default-resolution logic starts appearing in more than one place, extract a small shared helper rather than repeating the literal default. Remember this may need bridging through the platform channel since this package delegates to native iOS/Android SDKs.

## Build & test

```bash
Expand Down
34 changes: 30 additions & 4 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@ group 'dev.thunderid.flutter'
version '0.0.0'

buildscript {
ext.kotlin_version = '1.9.22'
ext.kotlin_version = '2.2.20'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.2.2'
classpath 'com.android.tools.build:gradle:8.11.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

repositories {
google()
mavenCentral()
maven {
url 'https://storage.googleapis.com/download.flutter.io'
}
maven {
url 'https://jitpack.io'
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

Expand All @@ -34,8 +45,23 @@ android {
}
}

def flutterSdkPath() {
def envRoot = System.getenv('FLUTTER_ROOT')
if (envRoot != null) {
return envRoot
}
def localPropertiesFile = new File(rootDir, '../samples/quickstart/android/local.properties')
def properties = new Properties()
localPropertiesFile.withInputStream { properties.load(it) }
def sdkPath = properties.getProperty('flutter.sdk')
assert sdkPath != null, "flutter.sdk not set in local.properties"
return sdkPath
}

def flutterEngineVersion = new File(flutterSdkPath(), 'bin/cache/engine.stamp').text.trim()

dependencies {
compileOnly 'io.flutter:flutter_embedding_debug:1.0.0-...'
implementation 'dev.thunderid:android:0.0.0'
compileOnly "io.flutter:flutter_embedding_debug:1.0.0-$flutterEngineVersion"
implementation 'com.github.thunder-id:android-sdks:v0.1.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
}
1 change: 1 addition & 0 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
android.useAndroidX=true
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ThunderIDMethodHandler(private val context: Context) {
when (method) {
"initialize" -> {
val config = buildConfig(args)
val storage = EncryptedStorageAdapter(context)
val storage = EncryptedStorageAdapter(context, prefsName = "dev.${config.vendor}.sdk.prefs")
result.success(client.initialize(config, storage))
}
"reInitialize" -> {
Expand Down Expand Up @@ -120,7 +120,8 @@ class ThunderIDMethodHandler(private val context: Context) {
afterSignInUrl = args["afterSignInUrl"] as? String,
afterSignOutUrl = args["afterSignOutUrl"] as? String,
applicationId = args["applicationId"] as? String,
tokenValidation = validation
tokenValidation = validation,
vendor = args["vendor"] as? String ?: ThunderIDConfig.DEFAULT_VENDOR
)
}

Expand Down
3 changes: 2 additions & 1 deletion ios/Classes/ThunderIDMethodHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ final class ThunderIDMethodHandler {
afterSignInUrl: args["afterSignInUrl"] as? String,
afterSignOutUrl: args["afterSignOutUrl"] as? String,
applicationId: args["applicationId"] as? String,
tokenValidation: validation
tokenValidation: validation,
vendor: args["vendor"] as? String ?? VendorConstants.vendorPrefix
)
}

Expand Down
10 changes: 10 additions & 0 deletions lib/src/models/thunderid_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import 'preferences.dart';

/// Configuration for the ThunderID Flutter SDK (spec §5.2).
class ThunderIDConfig {
/// Default vendor/brand namespace used when [vendor] is not overridden.
static const String defaultVendor = 'thunderid';

// Core
final String baseUrl;
final String? clientId;
Expand All @@ -46,6 +49,11 @@ class ThunderIDConfig {
// UI Preferences (theme + i18n) — ignored by the protocol layer
final ThunderIDPreferences? preferences;

/// Vendor/brand namespace used by the native SDK layer to derive default storage identifiers.
/// Override this when white-labeling the SDK under a different brand. Defaults to
/// [defaultVendor].
final String vendor;

const ThunderIDConfig({
required this.baseUrl,
this.clientId,
Expand All @@ -61,6 +69,7 @@ class ThunderIDConfig {
this.organizationHandle,
this.tokenValidation = const TokenValidationConfig(),
this.preferences,
this.vendor = defaultVendor,
});

Map<String, dynamic> toMap() => {
Expand All @@ -78,6 +87,7 @@ class ThunderIDConfig {
if (organizationHandle != null) 'organizationHandle': organizationHandle,
'tokenValidation': tokenValidation.toMap(),
if (preferences != null) 'preferences': preferences!.toMap(),
'vendor': vendor,
};
}

Expand Down
11 changes: 2 additions & 9 deletions samples/quickstart/android/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,8 @@ pluginManagement {

plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.2.2" apply false
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
}

includeBuild("../../../android") {
name = "android"
dependencySubstitution {
substitute(module("dev.thunderid:android")).using(project(":"))
}
id("com.android.application") version "8.11.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
}

include(":app")
Loading