├── .gitattributes ├── .github └── workflows │ └── github-actions.yml ├── .gitignore ├── Add SecureToken to Target User.sh ├── LICENSE ├── README.md └── SecureToken Status - Logged-In User.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.github/workflows/github-actions.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | shellcheck: 11 | name: ShellCheck 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out 15 | uses: actions/checkout@v2 16 | - name: Shell Linter 17 | uses: azohra/shell-linter@latest 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .DS_Store files 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /Add SecureToken to Target User.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ### 4 | # 5 | # Name: Add SecureToken to Target User.sh 6 | # Description: This script adds a SecureToken to the target local user to prepare the Mac for enabling FileVault. Prompts for password of SecureToken admin (gets SecureToken Admin Username from Jamf Pro script parameter) and target user. This workflow is required to authorize programmatically-created user accounts (that were not already explicitly given a SecureToken) to enable or use FileVault and unlock disk encryption on APFS-formatted startup volumes. 7 | # https://github.com/mpanighetti/add-securetoken-to-target-user 8 | # 9 | # MIT License 10 | # 11 | # Copyright (c) 2017 Mario Panighetti 12 | # 13 | # Permission is hereby granted, free of charge, to any person obtaining a copy 14 | # of this software and associated documentation files (the "Software"), to deal 15 | # in the Software without restriction, including without limitation the rights 16 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | # copies of the Software, and to permit persons to whom the Software is 18 | # furnished to do so, subject to the following conditions: 19 | # 20 | # The above copyright notice and this permission notice shall be included in all 21 | # copies or substantial portions of the Software. 22 | # 23 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | # SOFTWARE. 30 | # 31 | # Author: Mario Panighetti 32 | # Created: 2017-10-04 33 | # Last Modified: 2025-04-28 34 | # Version: 4.1 35 | # 36 | ### 37 | 38 | 39 | 40 | ########## variable-ing ########## 41 | 42 | 43 | 44 | # Jamf Pro script parameter: "Target Username" 45 | # (optional) A local user account requiring SecureToken. If undefined, script will default to the logged-in user as the target. 46 | targetUsername="${4}" 47 | # Jamf Pro script parameter: "SecureToken Admin Username" 48 | # A local administrator account with SecureToken access. 49 | secureTokenAdmin="${5}" 50 | macOSVersionMajor=$(/usr/bin/sw_vers -productVersion | /usr/bin/awk -F . '{print $1}') 51 | macOSVersionMinor=$(/usr/bin/sw_vers -productVersion | /usr/bin/awk -F . '{print $2}') 52 | macOSVersionBuild=$(/usr/bin/sw_vers -productVersion | /usr/bin/awk -F . '{print $3}') 53 | # Need default password values so the initial logic loops will properly fail when validating passwords. You can store the actual credentials here to skip password prompts entirely, but for security reasons this is not generally recommended. Please don't actually use "foo" as a password, for so many reasons. 54 | secureTokenAdminPass="foo" 55 | targetUserPassword="foo" 56 | passwordPrompt="foo" 57 | 58 | 59 | 60 | ########## function-ing ########## 61 | 62 | 63 | 64 | # Exits with error if any required Jamf Pro arguments are undefined. 65 | check_jamf_pro_arguments () { 66 | 67 | if [ -z "$secureTokenAdmin" ]; then 68 | echo "❌ ERROR: Undefined Jamf Pro argument, unable to proceed." 69 | exit 74 70 | fi 71 | 72 | } 73 | 74 | 75 | # Exits if macOS version predates the use of SecureToken functionality. 76 | check_macos_version () { 77 | 78 | # Exit if macOS < 10. 79 | if [ "$macOSVersionMajor" -lt 10 ]; then 80 | echo "macOS version ${macOSVersionMajor} predates the use of SecureToken functionality, no action required." 81 | exit 0 82 | # Exit if macOS 10 < 10.13.4. 83 | elif [ "$macOSVersionMajor" -eq 10 ]; then 84 | if [ "$macOSVersionMinor" -lt 13 ]; then 85 | echo "macOS version ${macOSVersionMajor}.${macOSVersionMinor} predates the use of SecureToken functionality, no action required." 86 | exit 0 87 | elif [ "$macOSVersionMinor" -eq 13 ] && [ "$macOSVersionBuild" -lt 4 ]; then 88 | echo "macOS version ${macOSVersionMajor}.${macOSVersionMinor}.${macOSVersionBuild} predates the use of SecureToken functionality, no action required." 89 | exit 0 90 | fi 91 | fi 92 | 93 | } 94 | 95 | 96 | # Sets target username to defined value from script parameter, or defaults to logged-in user if undefined. 97 | check_target_username () { 98 | 99 | if [ -z "$targetUsername" ]; then 100 | echo "Target Username undefined. Defaulting to logged-in user..." 101 | loggedInUser=$(/usr/bin/stat -f%Su "/dev/console") 102 | # Exit if root is the current logged-in user, or no logged-in user is detected. 103 | if [ "$loggedInUser" = "root" ] || [ -z "$loggedInUser" ]; then 104 | echo "Nobody is logged in." 105 | exit 0 106 | else 107 | targetUsername="$loggedInUser" 108 | fi 109 | elif id -u "$targetUsername"; then 110 | echo "Confirmed target user exists on this Mac." 111 | else 112 | echo "❌ ERROR: User not found on this Mac, unable to proceed: ${targetUsername}" 113 | exit 1 114 | fi 115 | echo "Target Username: ${targetUsername}" 116 | 117 | } 118 | 119 | 120 | # Exits if target user already has SecureToken. 121 | check_securetoken_target_user () { 122 | 123 | if /usr/sbin/sysadminctl -secureTokenStatus "$targetUsername" 2>&1 | /usr/bin/grep -q "ENABLED"; then 124 | echo "${targetUsername} already has a SecureToken, no action required." 125 | exit 0 126 | fi 127 | 128 | } 129 | 130 | 131 | # Exits with error if $secureTokenAdmin does not have SecureToken (unless running macOS 10.15 or later, in which case exit with explanation). 132 | check_securetoken_admin () { 133 | 134 | if /usr/sbin/sysadminctl -secureTokenStatus "$secureTokenAdmin" 2>&1 | /usr/bin/grep -q "DISABLED" ; then 135 | if [ "$macOSVersionMajor" -gt 10 ] || [ "$macOSVersionMajor" -eq 10 ] && [ "$macOSVersionMinor" -gt 14 ]; then 136 | echo "⚠️ Neither ${secureTokenAdmin} nor ${targetUsername} has a SecureToken, but in macOS 10.15 or later, a SecureToken is automatically granted to the first user to enable FileVault (if no other users have SecureToken), so this may not be necessary. Try enabling FileVault for ${targetUsername}. If that fails, see what other user on the system has SecureToken, and use its credentials to grant SecureToken to ${targetUsername}." 137 | exit 0 138 | else 139 | echo "❌ ERROR: ${secureTokenAdmin} does not have a valid SecureToken, unable to proceed. Please update Jamf Pro policy to target another admin user with SecureToken." 140 | exit 1 141 | fi 142 | else 143 | echo "✅ Verified ${secureTokenAdmin} has SecureToken." 144 | fi 145 | 146 | } 147 | 148 | 149 | # Prompts for local password. 150 | local_account_password_prompt () { 151 | 152 | passwordPrompt=$(/usr/bin/osascript -e "set user_password to text returned of (display dialog \"${2}\" default answer \"\" with hidden answer)") 153 | if [ -z "$passwordPrompt" ]; then 154 | echo "❌ ERROR: A password was not entered for ${1}, unable to proceed. Please rerun policy; if issue persists, a manual SecureToken add will be required to continue." 155 | exit 1 156 | fi 157 | 158 | } 159 | 160 | 161 | # Validates provided password. 162 | local_account_password_validation () { 163 | 164 | if /usr/bin/dscl "/Local/Default" authonly "${1}" "${2}" > "/dev/null" 2>&1; then 165 | echo "✅ Password successfully validated for ${1}." 166 | else 167 | echo "❌ ERROR: Failed password validation for ${1}. Please reenter the password when prompted." 168 | fi 169 | 170 | } 171 | 172 | 173 | # Adds SecureToken to target user. 174 | securetoken_add () { 175 | 176 | /usr/sbin/sysadminctl \ 177 | -adminUser "${1}" \ 178 | -adminPassword "${2}" \ 179 | -secureTokenOn "${3}" \ 180 | -password "${4}" 181 | 182 | # Verify successful SecureToken add. 183 | secureTokenCheck=$(/usr/sbin/sysadminctl -secureTokenStatus "${3}" 2>&1) 184 | if echo "$secureTokenCheck" | /usr/bin/grep -q "DISABLED"; then 185 | echo "❌ ERROR: Failed to add SecureToken to ${3}. Please rerun policy; if issue persists, a manual SecureToken add will be required to continue." 186 | exit 126 187 | elif echo "$secureTokenCheck" | /usr/bin/grep -q "ENABLED"; then 188 | echo "Successfully added SecureToken to ${3}." 189 | else 190 | echo "❌ ERROR: Unexpected result, unable to proceed. Please rerun policy; if issue persists, a manual SecureToken add will be required to continue." 191 | exit 1 192 | fi 193 | 194 | } 195 | 196 | 197 | 198 | ########## main process ########## 199 | 200 | 201 | 202 | # Check script prerequisites. 203 | check_jamf_pro_arguments 204 | check_macos_version 205 | check_target_username 206 | check_securetoken_target_user 207 | check_securetoken_admin 208 | 209 | 210 | # Add SecureToken to target user. 211 | until /usr/sbin/sysadminctl -secureTokenStatus "$targetUsername" 2>&1 | /usr/bin/grep -q "ENABLED"; do 212 | 213 | # Get $secureTokenAdmin password. 214 | echo "${targetUsername} missing SecureToken, prompting for credentials..." 215 | until /usr/bin/dscl "/Local/Default" authonly "$secureTokenAdmin" "$secureTokenAdminPass" > "/dev/null" 2>&1; do 216 | local_account_password_prompt "$secureTokenAdmin" "Please enter password for ${secureTokenAdmin}. User's credentials are needed to grant a SecureToken to ${targetUsername}." 217 | secureTokenAdminPass="$passwordPrompt" 218 | local_account_password_validation "$secureTokenAdmin" "$secureTokenAdminPass" 219 | done 220 | 221 | # Get target user's password. 222 | until /usr/bin/dscl "/Local/Default" authonly "$targetUsername" "$targetUserPassword" > "/dev/null" 2>&1; do 223 | local_account_password_prompt "$targetUsername" "Please enter password for ${targetUsername} to add SecureToken." 224 | targetUserPassword="$passwordPrompt" 225 | local_account_password_validation "$targetUsername" "$targetUserPassword" 226 | done 227 | 228 | # Add SecureToken using provided credentials. 229 | securetoken_add "$secureTokenAdmin" "$secureTokenAdminPass" "$targetUsername" "$targetUserPassword" 230 | 231 | done 232 | 233 | 234 | # Echo successful result. 235 | echo "✅ Verified SecureToken is enabled for ${targetUsername}." 236 | 237 | 238 | 239 | exit 0 240 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mario Panighetti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Add SecureToken to Target User 2 | 3 | This project includes workflows for adding SecureToken to a target local user. This workflow is required to authorize programmatically-created user accounts (that were not already explicitly given a SecureToken) to enable or use FileVault and unlock disk encryption on APFS-formatted startup volumes. 4 | 5 | ## Script 6 | 7 | This script adds a SecureToken to the target local user to prepare the Mac for enabling FileVault. Prompts for password of SecureToken admin (gets SecureToken Admin Username from Jamf Pro script parameter) and target user. 8 | 9 | ## Extension Attribute 10 | 11 | **SecureToken Status - Logged-In User** is a Jamf Pro extension attribute (see [Computer Extension Attributes](https://learn.jamf.com/en-US/bundle/jamf-pro-documentation-current/page/Computer_Extension_Attributes.html) in Jamf Pro Documentation). After uploading this extension attribute to Jamf Pro, you can target a policy running this repository's main script at a smart computer group of Macs where the logged-in user has a value of `DISABLED` for this script's output. Once a SecureToken has been added to the target user, this script should report `ENABLED` if everything ran as expected. 12 | 13 | ## Credits 14 | 15 | - `sysadminctl` SecureToken syntax discovered and formalized in [MacAdmins Slack](https://macadmins.slack.com) #filevault. 16 | 17 | ## License 18 | 19 | This project is offered under an MIT License. 20 | -------------------------------------------------------------------------------- /SecureToken Status - Logged-In User.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ### 4 | # 5 | # Name: SecureToken Status - Logged-In User.sh 6 | # Description: Reports whether SecureToken is enabled for the currently logged-in user. 7 | # https://github.com/mpanighetti/add-securetoken-to-logged-in-user 8 | # 9 | # MIT License 10 | # 11 | # Copyright (c) 2017 Mario Panighetti 12 | # 13 | # Permission is hereby granted, free of charge, to any person obtaining a copy 14 | # of this software and associated documentation files (the "Software"), to deal 15 | # in the Software without restriction, including without limitation the rights 16 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | # copies of the Software, and to permit persons to whom the Software is 18 | # furnished to do so, subject to the following conditions: 19 | # 20 | # The above copyright notice and this permission notice shall be included in all 21 | # copies or substantial portions of the Software. 22 | # 23 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | # SOFTWARE. 30 | # 31 | # Author: Mario Panighetti 32 | # Created: 2022-02-08 33 | # Last Modified: 2025-04-28 34 | # Version: 1.0.2 35 | # 36 | ### 37 | 38 | 39 | 40 | ########## variable-ing ########## 41 | 42 | 43 | 44 | loggedInUser=$(/usr/bin/stat -f%Su "/dev/console") 45 | 46 | 47 | 48 | ########## main process ########## 49 | 50 | 51 | 52 | # Check SecureToken for currently logged-in user and report results. 53 | echo "$(/usr/sbin/sysadminctl -secureTokenStatus "$loggedInUser" 2>&1 | /usr/bin/awk '{print $7}')" 54 | 55 | 56 | 57 | exit 0 58 | --------------------------------------------------------------------------------