Lexikjwtauthenticationbundle: `secret_key` and `public_key` usage from environment variables

Created on 4 Jul 2018  路  13Comments  路  Source: lexik/LexikJWTAuthenticationBundle

Hi there !

First of all thanks for this great library !
I've been watching the PR to bring new way to pass private & public keys: environment var instead of file reference.

This will make me delete a volume that we're using in production to share the keys to all the running container. Passing by the environment file make that much easier 馃憤

So, I updated the composer.json dependency to the following: "lexik/jwt-authentication-bundle": "~2.5".

Configuration files

And my configuration is the following:
parameters.yml

parameters:
  jwt.secret_key: '%env(JWT_SECRET_KEY)%'
  jwt.public_key: '%env(JWT_PUBLIC_KEY)%'
  jwt.pass_phrase: '%env(JWT_PASS_PHRASE)%'
  jwt.token_ttl: '%env(JWT_TOKEN_TTL)%'

config.yml

lexik_jwt_authentication:
    secret_key: '%jwt.secret_key%'
    public_key: '%jwt.public_key%'
    pass_phrase: '%jwt.pass_phrase%'
    token_ttl: '%jwt.token_ttl%'

Environment var file

JWT_SECRET_KEY=""
JWT_PUBLIC_KEY=""
JWT_PASS_PHRASE=password
JWT_TOKEN_TTL=3600

Error message

The error message I have is:

{
    "error": {
        "code": 500,
        "message": "Internal Server Error",
        "exception": [
            {
                "message": "Could not read key resource: error:0906D06C:PEM routines:PEM_read_bio:no start line",
                "class": "RuntimeException",
                "trace": [
                    {
                        "namespace": "",
                        "short_class": "",
                        "class": "",
                        "type": "",
                        "function": "",
                        "file": "/var/www/html/vendor/namshi/jose/src/Namshi/JOSE/Signer/OpenSSL/PublicKey.php",
                        "line": 65,
                        "args": []
                    },
                    {
                        "namespace": "Namshi\\JOSE\\Signer\\OpenSSL",
                        "short_class": "PublicKey",
                        "class": "Namshi\\JOSE\\Signer\\OpenSSL\\PublicKey",
                        "type": "->",
                        "function": "getKeyResource",
                        "file": "/var/www/html/vendor/namshi/jose/src/Namshi/JOSE/Signer/OpenSSL/PublicKey.php",
                        "line": 19,
                        "args": [
                            [
                                "string",
                                "\"-----BEGIN RSA PRIVATE KEY-----"
                            ],
                            [
                                "string",
                                "password"
                            ]
                        ]
                    },
                    {
                        "namespace": "Namshi\\JOSE\\Signer\\OpenSSL",
                        "short_class": "PublicKey",
                        "class": "Namshi\\JOSE\\Signer\\OpenSSL\\PublicKey",
                        "type": "->",
                        "function": "sign",
                        "file": "/var/www/html/vendor/namshi/jose/src/Namshi/JOSE/JWS.php",
                        "line": 60,
                        "args": [
                            [
                                "string",
                                "eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyIjoie1widXNlcm5hbWVcIjpcImouZG9lMVwiLFwiZW1haWxcIjpcInJlaWNoZWwuemV0dGFAaG[.......]vc3RDb252ZW50aW9uRHJhZnRBY3Rpb24iLCJDb2xsZWN0aW9uc0NvbnRyb2xsZXI6OnB1dENvbGxlY3Rpb25DbGluaWNzUmF0ZXNBY3Rpb24iLCJFeGNlcHRpb25Db250cm9sbGVyOjpzaG93QWN0aW9uIl0sImV4cCI6IjE1MzA3MTcxMDEiLCJpYXQiOjE1MzA3MTM1MDJ9"
                            ],
                            [
                                "string",
                                "\"-----BEGIN RSA PRIVATE KEY-----"
                            ],
                            [
                                "string",
                                "password"
                            ]
                        ]
                    },
                    {
                        "namespace": "Namshi\\JOSE",
                        "short_class": "JWS",
                        "class": "Namshi\\JOSE\\JWS",
                        "type": "->",
                        "function": "sign",
                        "file": "/var/www/html/vendor/lexik/jwt-authentication-bundle/Services/JWSProvider/DefaultJWSProvider.php",
                        "line": 99,
                        "args": [
                            [
                                "string",
                                "\"-----BEGIN RSA PRIVATE KEY-----"
                            ],
                            [
                                "string",
                                "password"
                            ]
                        ]
                    },
                ]
            }
        ]
    }
}

My problem

The problem I have is that I don't know how to format the key in the environment var file. A one line format don't work at all. Doing stuff like:

JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA487nB+V2ZvM53ZvonSb8\
T6zR3vaZYufa0dsPn[......]F8CAwEAAQ==\
-----END PUBLIC KEY-----"

Or even like:

JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA487nB+V2ZvM53ZvonSb8
T6zR3vaZYufa0dsPn[......]F8CAwEAAQ==
-----END PUBLIC KEY-----"

Key generation

My private & public keys are generated like that. It works fine, since I was able to use them with filepath configuration before. My problem is really about the correct formatting for the keys on the environment var file.

openssl genrsa -passout pass:password -out var/jwt/private.pem -aes256 4096
openssl rsa -passin pass:password -pubout -in var/jwt/private.pem -out var/jwt/public.pem

Help :D

So, I don't know how to put the keys in there. I feel like I'm close to a solution, but I can't figure it out...
Also looked at the documentation, but nothing useful on there (at least for my case !).

I'll love to update the documentation once a solution is found :)

Thanks a lot in advance fellas !

Most helpful comment

I currently use the base64 encoded version of the certs in my env vars. So in the config I have:

lexik_jwt_authentication:
    secret_key: '%env(base64:JWT_PRIVATE_KEY)%'
    public_key: '%env(base64:JWT_PUBLIC_KEY)%'
    pass_phrase: '%env(JWT_PASSPHRASE)%'

This converts the base64 to the original file contents.

To get the value for the env vars, you can use the output of the next command:

$ cat config/jwt/public.pem | base64 | tr -d '\n'

Alternative

Use some more environment variable processors and use the following:

JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem

And load it by

# ...
public_key: '%env(file:resolve:JWT_PUBLIC_KEY)%

Note: I did not test this alternative, but it should work.

Hope this helps!

All 13 comments

Hey, glad to have some feedback about that feature, that will help us documenting it.
Does the following solution work in your case https://stackoverflow.com/a/49489260/4363634?

Hi @chalasr thanks for your response !

I did try, and it doesn't works.
Here a little more context.

docker-compose file

Here is a quick extract. Simple, nothing strange there, but worth showing.

backend:
    build:
      context: .
    env_file:
      - build/env/configuration.env

Latest test

With the linked stackoverflow topic, here is the result I have for my environment file:

JWT_SECRET_KEY='-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,B4DDC5E81CCB984691F6B678CD30AEFF

5DDzO68lT2k53kbZ4aMapZ0GTXBHIEEvZb+uhk5idCYQw8BN0oE8gEN4FKkfBbrW
2I5raATnb8f3bUfqFWBsLwis6Moqz4FCqWwYJeCrisKnE2xRKqPXUj1XU0rg+W9a
+vRjZDH4KJMmm398y20DfMBTI7dEt/1X0X7aVGPONldNaDAtua+KleRVWdzMT0hO
o0vhS6ba1YHEEVU/YdxzDDE9lxQE0qjxDLTR90FfcCJNkLx2yqM2ztRdpasrnqc0
G4t/E89O2HmJZim1L/sG2aRTB1wKoPgbHzx6gcZ0O8YuTX+w6EgnH37nyvwdmBIc
ZjW8ID1hWADkEGM9exZf0ABdu+1vmn3By2w1skS54uW3qPVAWVwYET8s1++5W+JX
lNrZ2Tb/EfnAqD/VgOmuwuwtLSWK8hrtXKr8pwTtOgdblZpXrVI2mBdbYptHzV83[...]'

I added a dumb debug line on the following file: vendor/lexik/jwt-authentication-bundle/Services/JWSProvider/DefaultJWSProvider.php:create(array $payload, array $header = [])

echo $this->keyLoader->loadKey('private');
die();

And with the previous env file, I give my the following result on PostMan:

'-----BEGIN RSA PRIVATE KEY-----

As you can see, the key is cut, which make it not usable.

The Exception raised is still the same, tho: "message": "Could not read key resource: error:0906D06C:PEM routines:PEM_read_bio:no start line",

Other tests

I also tested like:

JWT_SECRET_KEY="-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,B4DDC5E81CCB984691F6B678CD30AEFF[....]"

But without any effect, the \n remain, which make the code unable to manipulate the key.

How did you test the code during the development process ? Did you used environment file ?
I believe multi-lines env directly on docker-compose.yml file works fine, with some escaping from the YAML magic. But it does not fit my needs, and can't be ready for production: all need to be on an environment file.

I'll continue to tests some other formatting & dig a bit into the code.
Any feedback is welcome, it's kind of a headache there :)

Original comment, but wrong:

Working solution with code modification

The only things I get to work right now is the following.

env file

JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA487nB+V2ZvM53ZvonSb8\nT6z[...]p9\nXPgycVfF3I0dsn+qkcg0rF8CAwEAAQ==\n-----END PUBLIC KEY-----"

php code

echo str_replace('\n', PHP_EOL, $_ENV['JWT_PUBLIC_KEY']); // this works: display multilines content & JWT encode / decode works

Without the ugly str_replace debug line, the error is still the same.

So ?

Do you think changing the library behaviour on the KeyLoader process will make sense ?
Or somewhere else on the code ?

UPDATE

Ok, it doesn't work at all, it was a bad interpretation...

app_dev.php debug lines

I did add those lines to the app_dev.php file, to do ugly debug & test stuff:

$_ENV['JWT_SECRET_KEY'] = str_replace('\n', PHP_EOL, $_ENV['JWT_SECRET_KEY']);
$_ENV['JWT_PUBLIC_KEY'] = str_replace('\n', PHP_EOL, $_ENV['JWT_PUBLIC_KEY']);

DefaultJWSProvider.php debug lines

The following debug code still in place on the DefaultJWSProvider.php file:

echo "PRIVATE KEY:\n";
echo $_ENV['JWT_SECRET_KEY'];

echo "---";

echo "\nPUBLIC KEY:\n";
echo $_ENV['JWT_PUBLIC_KEY'];

die();

Results

And the result is:

PRIVATE KEY:
"-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,B4DDC5E81CCB984691F6B678CD30AEFF
5DDzO68lT2k53[....]2uezJbEZ9tCPkh3SwwHQv/5lms3tXi9zBA+/mcOPvCy+82xqC4x
-----END RSA PRIVATE KEY-----"---
PUBLIC KEY:
"-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA487nB+V2ZvM53ZvonSb8
T6zR3vaZYufa0dsPnp7MxO1PFlMAMwArcHDEhvp1e7CSMCsy0MvS7KM7RvKMq978
pO7CwhJ97I6RC[...]+qkcg0rF8CAwEAAQ==
-----END PUBLIC KEY-----"

Keys are correctly displayed on RAW content, but it's still not working...
I'm out of idea for today :/

New week, new me.

Didn't find any new way to make it works right now :(

Ok, any update or news ? :)

@piteur Your keys are encrypted. Try with a non-encrypted key

@agathver - I don't see the point there.
My keys are encrypted and should stay encrypted. I don't want to use non-encrypted keys, this is madness :)

I worked before the new release with the old-style parameters (with path to certificate file), and the keys were encrypted. And it worked.

Anyway, thanks for your input.

I currently use the base64 encoded version of the certs in my env vars. So in the config I have:

lexik_jwt_authentication:
    secret_key: '%env(base64:JWT_PRIVATE_KEY)%'
    public_key: '%env(base64:JWT_PUBLIC_KEY)%'
    pass_phrase: '%env(JWT_PASSPHRASE)%'

This converts the base64 to the original file contents.

To get the value for the env vars, you can use the output of the next command:

$ cat config/jwt/public.pem | base64 | tr -d '\n'

Alternative

Use some more environment variable processors and use the following:

JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem

And load it by

# ...
public_key: '%env(file:resolve:JWT_PUBLIC_KEY)%

Note: I did not test this alternative, but it should work.

Hope this helps!

Thanks a lot for the feedback & ideas @TV-productions - I'll give it a shot this week for the base64 approach.

Really appreciate !

I just tried with the base64 approach, and it works! thanks

Also to override the key path config and use the environment vars for test environment only use

lexik_jwt_authentication:
    private_key_path: ~
    public_key_path:  ~
    secret_key: '%env(base64:JWT_PRIVATE_KEY)%'
    public_key: '%env(base64:JWT_PUBLIC_KEY)%'

@PaddyLock note that the private_key_path and public_key_path options are not necessary and should be removed (as they won't exist anymore in the next major release).

@chalasr thanks, so should we use secret_key and public_key for both a path to a file and/or the raw key data?

exactly! (you should get a proper deprecation notice when setting the key)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Zempheroth picture Zempheroth  路  4Comments

tahina4 picture tahina4  路  4Comments

christophe-mailfert picture christophe-mailfert  路  5Comments

kemicofa picture kemicofa  路  5Comments

ghost picture ghost  路  5Comments