Closed ospaarmann closed 8 years ago
I believe this has to do with how and when configuration files are evaluated and compiled. @ospaarmann can you try putting it in an anonymous function?
config :guardian, Guardian,
...
secret_key: fn -> System.get_env("GUARDIAN_KEY_PASSPHRASE") |> JOSE.JWK.from_file(System.get_env("GUARDIAN_KEY_FILE")) end,
serializer: MyApp.GuardianSerializer
This compiles. But now the config value is the function itself and not what it returns:
iex(2)> Guardian.config
[allowed_algos: ["HS512"], verify_module: Guardian.JWT, issuer: "MyApp",
ttl: {30, :days}, verify_issuer: true,
secret_key: #Function<20.50752066/0 in :erl_eval.expr/5>,
serializer: MyApp.GuardianSerializer]
iex(3)> Guardian.config(:secret_key)
#Function<20.50752066/0 in :erl_eval.expr/5>
@ospaarmann what about Guardian.config(:secret_key).()
?
This returns the correct value but I believe the Guardian configuration itself is still not correct. No?
@ospaarmann JOSE accepts functions: https://github.com/ueberauth/guardian#secret-key.
Have you tried using the configuration?
No. I have to check some other things since I'm not using Ecto but will report back. Thanks for the help
I tried this in another Phoenix App that uses Ecto and is less custom-made. It does not work. The error I'm getting is
[error] #PID<0.379.0> running MyApp.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /api/v1/sessions
** (exit) an exception was raised:
** (FunctionClauseError) no function clause matching in :base64url.encode/1
src/base64url.erl:22: :base64url.encode(#Function<20.50752066/0 in :erl_eval.expr/5>)
lib/guardian.ex:291: Guardian.jose_jwk/1
lib/guardian.ex:298: Guardian.encode_claims/1
lib/guardian.ex:89: Guardian.encode_and_sign/3
(data_bucket) web/controllers/session_controller.ex:15: MyApp.SessionController.create/2
(data_bucket) web/controllers/session_controller.ex:1: MyApp.SessionController.action/2
(data_bucket) web/controllers/session_controller.ex:1: MyApp.SessionController.phoenix_controller_pipeline/2
(data_bucket) lib/phoenix/router.ex:261: MyApp.Router.dispatch/2
(data_bucket) web/router.ex:1: MyApp.Router.do_call/2
(data_bucket) lib/data_bucket/endpoint.ex:1: MyApp.Endpoint.phoenix_pipeline/1
(data_bucket) lib/plug/debugger.ex:93: MyApp.Endpoint."call (overridable 3)"/2
(data_bucket) lib/phoenix/endpoint/render_errors.ex:34: MyApp.Endpoint.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4
So encoding the claims with the JWK fails and there again we find the reference to the anonymous function.
So I found the problem. The Readme claims that functions can be passed as Guardian.config(:secret_key) but the necessary changes are only present in the master branch and not in the latest release. They were introduced with this pull request.
I suggest to either create a release with these changes or mention this in the Readme to save other people some time :)
Is the code up to date? Key generation is not at all clear in the docs, as the link to Examples: Key Generation has 8 different methods. Which one do I use?
This is what I did to generate a password protected JWK file (which contains the generated key):
mix phoenix.gen.secret
touch .jwk_file
The first line just generates a secret I'm using as a password. And .jwk_file is the file to store the jwk in. You can name it whatever you want.
iex -S mix phoenix.server
iex(1)> jwk = JOSE.JWK.generate_key({:ec, :secp521r1})
iex(2)> password = "The Secret we just generated"
iex(3)> file = ".jwk_file"
iex(4)> JOSE.JWK.to_file(password, file, jwk)
Then in your config.exs
config :guardian, Guardian,
allowed_algos: ["HS512"], # optional
verify_module: Guardian.JWT, # optional
issuer: "MyApp",
ttl: { 30, :days },
verify_issuer: true, # optional
secret_key: fn ->
{jwe, jwk} = System.get_env("GUARDIAN_JWK_PASSPHRASE") |> JOSE.JWK.from_file(System.get_env("GUARDIAN_JWK_FILE"))
jwk
end,
serializer: MyApp.GuardianSerializer
Of course you have to pass in your environment variables. So GUARDIAN_JWK_PASSPHRASE should be the password (secret generated earlier) and GUARDIAN_JWK_FILE should be '.jwk_file' in this example.
The pattern matching is used because JOSE.JWK.from_file/2 returns the jwk plus the standard jwe and we have to fish out the jwk. And we have to wrap it in an anonymous function because of the way the configuration is parsed (before everything else, so JOSE.JWK would not be available otherwise).
Thanks, I've had to hard code the passphrase in as I don't know about env variables yet, but it seemed to compile
@ospaarmann The new secret key stuff in the readme is not yet released which is why you'll be having some trouble with it. I'll try and get a release cut soon to update the released guardian to match the readme.
Guardian 0.11.1 has been released with all the new JWT goodness.
When following the examples in the Readme.md for using JOSE.JWK to create / read in keys from a password protected file I always encounter the error
This is the code in my config.exs