@passageidentity/passage-flex-react-native
Version:
Passkey Flex for React Native - Add native passkey authentication to your own React Native authentication flows with Passage by 1Password
160 lines (150 loc) • 5.32 kB
JavaScript
const {
withDangerousMod,
withPlugins,
withAndroidManifest,
withEntitlementsPlist,
} = require('@expo/config-plugins');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
// Declare native Flex dependencies
const flexAndroidVersion = '0.2.0';
const gradelDependency = `implementation 'id.passage.android:passageflex:${flexAndroidVersion}'`;
const flexIosVersion = '0.2.0';
const podDependency = `pod 'PassageFlex', '${flexIosVersion}'`;
// iOS: Add PassageFlex CocoaPod
function withIosPodfile(configuration) {
return withDangerousMod(configuration, [
'ios',
(config) => {
const podfilePath = path.join(
config.modRequest.platformProjectRoot,
'Podfile'
);
let podfileContents = fs.readFileSync(podfilePath, 'utf-8');
if (!podfileContents.includes(podDependency)) {
const targetIndex = podfileContents.lastIndexOf(`target '`);
if (targetIndex !== -1) {
const nextLineIndex = podfileContents.indexOf('\n', targetIndex);
podfileContents =
podfileContents.slice(0, nextLineIndex) +
` ${podDependency}\n` +
podfileContents.slice(nextLineIndex);
}
}
fs.writeFileSync(podfilePath, podfileContents);
return config;
},
]);
}
// iOS: Add Associated Domains Capability
function withIosAssociatedDomain(configuration) {
const associatedDomain = process.env.ASSOCIATED_DOMAIN;
if (!associatedDomain) {
return configuration;
}
return withEntitlementsPlist(configuration, (config) => {
const key = 'com.apple.developer.associated-domains';
const webCredEntitlement = `webcredentials:${associatedDomain}`;
// Get existing associated domains if they exist
let domains = config.modResults[key] || [];
// Add the new domain only if it's not already present
if (!domains.includes(webCredEntitlement)) {
domains.push(webCredEntitlement);
}
config.modResults[key] = domains;
return config;
});
}
// Android: Add PassageFlex Gradle Dependency
function withAndroidGradleDependency(configuration) {
return withDangerousMod(configuration, [
'android',
(config) => {
const buildGradlePath = path.join(
config.modRequest.platformProjectRoot,
'app',
'build.gradle'
);
let buildGradleContents = fs.readFileSync(buildGradlePath, 'utf-8');
if (!buildGradleContents.includes(gradelDependency)) {
const dependenciesIndex = buildGradleContents.indexOf('dependencies {');
if (dependenciesIndex !== -1) {
const nextLineIndex = buildGradleContents.indexOf(
'\n',
dependenciesIndex
);
buildGradleContents =
buildGradleContents.slice(0, nextLineIndex + 1) +
` ${gradelDependency}\n` +
buildGradleContents.slice(nextLineIndex + 1);
}
}
fs.writeFileSync(buildGradlePath, buildGradleContents);
return config;
},
]);
}
// Android: Modify AndroidManifest.xml for asset_statements
function withAndroidAssociatedDomain(configuration) {
const associatedDomain = process.env.ASSOCIATED_DOMAIN;
if (!associatedDomain) {
return configuration;
}
// Modify AndroidManifest.xml
configuration = withAndroidManifest(configuration, async (config) => {
const application = config.modResults.manifest.application[0];
if (!application.hasOwnProperty('meta-data')) {
application['meta-data'] = [];
}
const metaDataExists = application['meta-data'].some(
(metaData) => metaData.$['android:name'] === 'asset_statements'
);
if (!metaDataExists) {
application['meta-data'].push({
$: {
'android:name': 'asset_statements',
'android:resource': '@string/asset_statements',
},
});
}
return config;
});
// Modify strings.xml
configuration = withDangerousMod(configuration, [
'android',
(config) => {
const stringsXmlPath = path.join(
config.modRequest.platformProjectRoot,
'app/src/main/res/values/strings.xml'
);
if (!fs.existsSync(stringsXmlPath)) {
throw new Error(`strings.xml not found at ${stringsXmlPath}`);
}
let stringsXmlContent = fs.readFileSync(stringsXmlPath, 'utf-8');
// Check if the string already exists
if (!stringsXmlContent.includes('<string name="asset_statements">')) {
const newStrings = `<string name="passage_auth_origin">${associatedDomain}</string>
<string name="asset_statements">[{"include": "https://@string/passage_auth_origin/.well-known/assetlinks.json"}]</string>`;
// Insert the new strings before the closing </resources> tag
stringsXmlContent = stringsXmlContent.replace(
'</resources>',
`${newStrings}\n</resources>`
);
fs.writeFileSync(stringsXmlPath, stringsXmlContent);
}
return config;
},
]);
return configuration;
}
// Combine all plugins into a single config plugin
const passageFlexPlugin = (configuration) => {
return withPlugins(configuration, [
withIosPodfile,
withIosAssociatedDomain,
withAndroidGradleDependency,
withAndroidAssociatedDomain,
]);
};
export default passageFlexPlugin;