I've recently had all sorts of issues building a Laravel app for Dokku because the Heroku buildpack it uses builds the application in an isolated container that has no access to the storage/
directory. I consistently found myself getting errors such as the following when running artisan commands:
Unable to read key from file file:///tmp/build/storage/oauth-public.key {"exception":"[object] (LogicException(code: 0): Unable to read key from file file:///tmp/build/storage/oauth-public.key at /tmp/build/vendor/league/oauth2-server/src/CryptKey.php:64)In CryptKey.php line 64:Unable to read key from file file:///tmp/build/storage/oauth-public.key
The main issue is that it was in a state where all artisan commands would fail, including php artisan passport:keys
, which would usually be used to generate these keys.
My solution is to create valid keys using the composer scripts, that at least allow your build to pass. After struggling a lot with the correct format, it seems the keys need to be generated by openssl
in the PEM format, with RSA encryption. Plain old ssh-keygen
won't work here. Here's the command to generate the keys:
openssl genpkey -algorithm RSA -out ./storage/oauth-private.key -pkeyopt rsa_keygen_bits:2048 openssl rsa -pubout -in ./storage/oauth-private.key -out ./storage/oauth-public.key
To actually make that effective, you need to add it to your composer.json
scripts
. I use the "compile"
scripts, as it's used by Heroku and Dokku, and isn't a standard composer scripts section.
Here's how your "scripts"
section might look:
{
// ...
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"if test \"$(pwd)\" != \"/tmp/build\"; then php artisan package:discover --ansi; fi"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall",
"if test \"$(pwd)\" != \"/tmp/build\"; then php artisan optimize; fi"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"if test \"$(pwd)\" != \"/tmp/build\"; then php artisan optimize; fi"
],
"compile": [
"if test ! -f 'storage/oauth-public.key'; then openssl genpkey -algorithm RSA -out ./storage/oauth-private.key -pkeyopt rsa_keygen_bits:2048 && openssl rsa -pubout -in ./storage/oauth-private.key -out ./storage/oauth-public.key; fi",
"@php artisan clear-compiled",
"@php artisan optimize",
"@php artisan migrate --force",
"@php artisan config:clear",
"@php artisan cache:clear"
]
}
}
I've highlighted the relevant line in bold, note it comes before any php artisan
commands.
There's an extra tidbit there too: the checks before running php artisan optimize
in the "post-install-cmd"
and "post-update-cmd scripts"
sections. This checks if we're running the code in the /tmp/build
, which would indicate we're in a Heroku build environment, and ensures we don't run code intended for development/CI environments. In these cases, the php artisan optimize
command would throw the same error.
This is obviously quite a hacky approach, but I couldn't find a better solution.
Discuss This
blog comments powered by Disqus