Build instructions?
I'm managing the IT of my small non-profit. We're very tight on budget, and since the new pricing and licencing update, we're not going to be able to use xcreds anymore.
We're able to compile our own apps, since we have a free (NPO) Apple Developper Account with all the required certificates to sign MacOS apps and installers.
However, I can't seem to be able to compile Xcreds on my own. Is there any documentation about how to build our own Xcreds version?
Okay, so I managed to compile XCreds without any issue. I could even customise some parts of it (force the domain name on login screen with the hd parameter). I've noticed some people on Macadmins Slack and Reddit that they were searching to compile it themselves.
Prerequisite
- A Developper ID Certificate (you can get one with the Apple Developper Program at $99/y)
- Xcode (install from AppStore)
- carthage (
brew install carthagein terminal) - git (
brew install git) - Packages (http://s.sudre.free.fr/Software/Packages/about.html)
Download the project(s)
Create an xcred directory. We need to download two different projects from twocanoes.
Open a terminal, and clone the two repositories:
git clone https://github.com/twocanoes/xcreds.git
git clone https://github.com/twocanoes/OIDCLite.git
[!NOTE] Twocanoes OIDCLite version is a fork from the original which allows custom parameters to be passed to the OIDC provider.
Prepare for compilation
Getting the latest stable version
In the same terminal, get into the xcreds repository, and checkout the latest version:
cd xcreds
git checkout v3.2.5197
Keep that terminal for later.
Download your signing certificates in Xcode
- Launch Xcode
- Open Xcode > Settings
- Under the Accounts tab, select (or login) your Apple Developer Account
- If you do not have requested your Developer ID Certificates yet, click "Manage Certificates...", click the + in the lower left corner and request both a Developer ID Application and a Developer ID Installer certificates
- Now, click Download Manual Profiles.
Your XCode is ready to use!
Modify Xcreds to use your signing certificates
- Open the xcreds repository in XCode. OIDCLite will be automatically detected and used
- In the left panel, double click the Xcreds app. This will open the Project preference plane. You should see 7 targets.
- For each target, except "Send to test", go to the Signing & Capabilities tab and under the red "Team" selectbox, choose your Apple Developer Account.
Xcreds will now be compiled using your certificates!
Modify keychain access using your TeamID
In the KeychainUtil.swift file under XCreds folder, find the let teamIds variable. Replace the teamid:XXXXXXXXX with your Apple Developer Team ID. You can find your team ID on the right of the header on your Certificates, Identifiers & Profiles page.
Disable the trial version
To be able to use Xcreds without time limit, you need to disable the licencing system. It is located in the Xcreds/LicenseChecker.swift.
Edit the LiceseChecker, locate the currentLicenseState() function, and add return .valid as the first line.
Prepare the Credits
Create the Credits.txt file under the XCreds repository (./xcreds/XCreds/Credits.txt) and write something in it. The Credits will be displayed in the "About" menu of XCreds desktop app. For instance, we wrote that:
Compiled by the Aureate Collective, restricted to non-profit usage only.
Compile XCreds app
Now you have Xcreds ready to compile, you just have to... Compile it. Hopefully, twocanoes prepared a compilation script that use XCode CLI to do (almost) everything for you. Get back to your terminal, and launch it:
./build.sh
When the script asks "post to github? (y/N)" just press enter (defaulting to no).
At the end of the compilation, you should see a line specifying the compilation result path, looking like:
output is in /tmp/XCreds.pspDioCb/build
This path contains the XCreds apps, ready to use.
Package XCreds and deploy it
XCreds packaging system is designed with the Packages app. This app is a packaging app, that allows to add payloads and apps to a PKG, and automatically sign it using your Apple ID Installer certificate.
- Open the Packages app previously installed
- Go to File > Open, and select the Packages project in the previously printed path: HD > tmp > XCreds.ojpaEFOj > build > Packages > XCreds > XCreds_template.pkgproj (Quick tip: you can display the
tmpfolder using Command + Shift + .) - Click the Project menu > Change certificate; and choose your Apple ID Installer.
- Change the path mode to Relative to Project (R) by clicking the blue F on the left of the textbox, and replace the path with
.(dot) - Change the Reference Folder to the correct
buildfolder (should be up to directories) - Open the Build menu, and click Build. Your custom signed and compiled
XCreds.pkgwill be in /tmp/XCreds.eaEJjoN/build/Packages/XCreds/build
You can now deploy that PKG to your MacOS fleet using a MDM (the PKG is signed with your own authorised certificate).
[!NOTE] Some MDM are able to deploy unsigned PKG, allowing you to save the $99/y Apple Developer Account. The steps should be pretty much the same, but you need to disable Automatic app/pkg signing in both XCode and Packages.
Wow, so nice of you to share the process you used to compile the app. I'm sure there are some other folks that will appreciate the work.
I hope it will help people to contribute to the project!
Update: I've been reported some issues with keychain when logging in using XCreds self compiled version. I've added the Modify keychain access using your TeamID subsection that fixes it.
I had some trouble compiling, adding some of what I've learned in case this is helpful for others:
- Be sure to checkout the most recent release branch for compilation.
maindoes not compile correctly. The latest branch right now isrelease-4.1. - You need to be the account holder to generate a Developer ID Certificate from Apple Dev Portal.
- Having the signed package is not needed to install it locally. Our use case for this software is running it on a common area Mac Mini that is only used one or twice a year - the only device in our org that this software is required for.
I always seem to get CompileSwift normal arm64 (in target 'XCredsLoginPlugin' from project 'xCreds')
or CompileSwift normal x86_64 (in target 'XCredsLoginPlugin' from project 'xCreds') when compiling to test. Not one build success at this point. :( Any ideas?
Edit: Building on an ARM Macbook Pro if that helps?
added BUILD.md
Here's a script to automate @Mavyre 's instructions:
- Change the BUNDLE_ID_PREFIX to your own ie 'com.mattisz'
- This script depends on xcode already having your Developer ID Application and Developer ID Installer certificates. Check out the "Download your signing certificates in Xcode" section in @Mavyre 's original instructions.
- cd to the directory you would like the git repository to live in (NOTE: if there is already a directory named xcreds here it will be deleted)
- Run the script and follow the prompts. You may get prompted for your account password for the parts that require permissions.
- Once complete, the build directory will open in finder. This is where you will find the packages to install.
#!/bin/zsh
export BUNDLE_ID_PREFIX='com.example'
#install homebrew if not installed
which brew
[ $? -ne 0 ] && /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
#install deps
brew install carthage git gsed
brew install --cask packages
#pull repo
rm -rf xcreds/
git clone https://github.com/twocanoes/xcreds.git
cd xcreds
#fix bundleid & signing team
grep -RlI 'com\.twocanoes' . | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "s/com\.twocanoes/$BUNDLE_ID_PREFIX/g"
id_full=$(security find-identity -v -p codesigning | grep -m1 "Developer ID Application" | sed -r 's/^.*:[[:space:]](.*)"$/\1/')
id=$(echo "$id_full" | sed -r 's/^.*\((.*)\).*$/\1/')
grep -RlI 'UXP6YEHSPW' . | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "s/UXP6YEHSPW/$id/g"
#rename files with bundleid
find . -iname '*com.twocanoes*' -exec bash -c 'old="{}"; new=$(echo $old | sed "s/com\.twocanoes/$BUNDLE_ID_PREFIX/"); mv "$old" "$new"' \;
#disable license check
file_line=$(grep -A1 -Rn 'func currentLicenseState' | tail -n1 | grep -o '.*[[:digit:]].*\-')
line_num=$(echo "$file_line" | grep -oE '[[:digit:]]+')
echo "$file_line" | sed 's/\-.*//' | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "${line_num}i\ return .valid"
#build
{ build_dir=$(./build.sh | tee /dev/fd/3 | tail -n5 | tac | grep -m1 'output is in' | sed 's/^output is in //'); } 3>&1
#fix packages
cd "$build_dir"
grep -RIl "Twocanoes Software, Inc. ($id)" | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "s/Twocanoes Software, Inc. ($id)/$id_full/"
grep -RIl "Library/Keychains/login.keychain" | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "s|<string>.*/Library/Keychains/login.keychain|<string>$HOME/Library/Keychains/login.keychain|"
IFS=$'\n\n' file_lines=($(grep -A10 -Rn '<key>BUILD_PATH</key>' | grep '<integer>3</integer>' | grep -o '.*[[:digit:]].*\-'))
for line in "${file_lines[@]}"; do line_num=$(echo "$line" | grep -oE '[[:digit:]]+'); echo "$line" | sed 's/\-.*//' | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "${line_num}s|3|1|"; done
IFS=$'\n\n' file_lines=($(grep -A10 -Rn '<key>BUILD_PATH</key>' | grep -A1 '<key>PATH</key>' | grep '<string>' | grep -o '.*[[:digit:]].*\-'))
for line in "${file_lines[@]}"; do line_num=$(echo "$line" | grep -oE '[[:digit:]]+'); echo "$line" | sed 's/\-.*//' | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "${line_num}s|<string>.*</string>|<string>.</string>|"; done
IFS=$'\n\n' file_lines=($(grep -A1 -Rn '<key>REFERENCE_FOLDER_PATH</key>' | grep '<string>' | grep -o '.*[[:digit:]].*\-'))
for line in "${file_lines[@]}"; do line_num=$(echo "$line" | grep -oE '[[:digit:]]+'); echo "$line" | sed 's/\-.*//' | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "${line_num}s|<string>.*</string>|<string>$build_dir</string>|"; done
IFS=$'\n\n' file_lines=($(grep -A2 -Rn '<key>PAYLOAD_ONLY</key>' | grep key | grep -v PAYLOAD_ONLY | grep -v REFERENCE_FOLDER_PATH | grep -o '.*[[:digit:]].*\-'))
for line in "${file_lines[@]}"; do line_num=$(echo "$line" | grep -oE '[[:digit:]]+'); echo "$line" | sed 's/\-.*//' | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "${line_num}i\ \t\t\t<key>REFERENCE_FOLDER_PATH</key>\n\t\t\t<string>$build_dir</string>"; done
# build packages
find . -iname "XCreds*.pkgproj*" | sed -e 's_\(.*\)_"\1"_g' | xargs -L1 packagesbuild -v
#open build dir
open "$build_dir"
@mattisz This worked just the once for me, thank you.
I'm now back to getting ** ARCHIVE FAILED ** errors with:
The following build commands failed:
SwiftCompile normal x86_64 Compiling...blah blah
**or**
SwiftCompile normal arm64 Compiling...blah blah
Archiving project xCreds with scheme XCreds
(3 failures)
CompileSwift normal x86_64 (in target 'XCredsLoginPlugin' from project 'xCreds')
**or**
CompileSwift normal arm64 (in target 'XCredsLoginPlugin' from project 'xCreds')
I'm not sure where to start diagnosing this issue. I perform a clean download and build each time.
@MacsInSpace Wish I could help you further but there are too many variables if our build environments don't match perfectly.
Try checking out the xcreds-5 branch. It's a bit behind main but may build correctly considering it hasn't been updated since I wrote that script.
I also just updated the script to be case insensitive when replacing UXP6YEHSPW and com.twocanoes. I'm not sure if that will have an impact but definitely grab the updated script.
@mattisz 5.1 built. Thanks for the tip.
I have changed the git clone line to:
git clone -b xcreds-5_1 https://github.com/twocanoes/xcreds.git
And also added
`# build
echo "set -e
carthage update xcodebuild -resolvePackageDependencies #-allowProvisioningUpdates
pushd ./build_resources/buildscripts/
SKIP_DMG=1 ./build.sh
popd " > ./build.sh`
So that it skips asking if you want to post it.
Thanks for the assist!
@MacsInSpace
To build v5.2 using the script that @mattisz provided, the license validator needed to be updated as it is expecting an integer. The integer would be the number of seconds remaining until the license expires:
echo "$file_line" | sed 's/\-.*//' | sed -e 's_\(.*\)_"\1"_g' | xargs gsed -i "${line_num}i\ return .valid(31536000)"
@serrc-techops Cheers.
With 5.1, We used
git clone -b xcreds-5_1 https://github.com/twocanoes/xcreds.git
for 5.2 there is no 5.2 (5_2) branch.
What are you using?
I've tried:
tagbranch="5.2.8251" git clone --no-checkout https://github.com/twocanoes/xcreds.git cd xcreds git checkout $tagbranch
..and continued from there (granted that I have not changed the line you recommended yet @serrc-techops )
@MacsInSpace
In this case I went with main via git clone https://github.com/twocanoes/xcreds.git which currently is v5.2.8270.
If you wanted a specific tag version I believe you should be able to go with git clone --branch <tag-name> --single-branch https://github.com/twocanoes/xcreds.git and you can see the available tags at https://github.com/twocanoes/xcreds/tags
@mattisz script with added return .valid(31536000) as above worked great for me with 5.2.8270 - just adding for anyone else who attempts that I was missing tac from the build command, so you may want to run brew install coreutils to get it beforehand
I used the script @mattisz provided with the xcreds-5_1 branch and while it did throw out errors mentioning the licensing module says the build was for macOS 12 and not 11, I corrected this in Xcode.
It did seem to build the app but gives me this error now: "Unable to copy item at path 'XCreds.app' to '/private/tmp/sW0tY5nV/502/XCreds_template/Applications' because the item could not be found".
When I do navigate to the tmp folder, I do have the app in one of the 4 "Xcred" folders in there but it doesn't seem to work correctly. I am unable to log into my domain account as indicated by my config profile. What could be wrong with my build?