Closed simonw closed 1 year ago
I'm going to do this with a shared secret between the development environments on Codespaces and the production https://www.pillarpointstewards.com/ website.
Then I'll construct a special auth0 callback URL like this:
/auth0-callback/aGVsbG8gdGhlcmU6VTdhZk03OWxDNVJzQXNiTzYtaWhZZWdiVHBSemxySE5ZcHhneW1XUE1Maw==
Relevant code:
That weird string in the path is a URL-safe base64 encoded signed string with the URL that everything should be forwarded BACK to once the authentication has completed.
Console exploration:
>>> from django.core import signing
>>> import base64
>>> msg = signing.Signer(key='shared').sign("hello there")
>>> msg
'hello there:U7afM79lC5RsAsbO6-ihYegbTpRzlrHNYpxgymWPMLk'
>>> base64.urlsafe_b64encode(msg.encode()).decode()
'aGVsbG8gdGhlcmU6VTdhZk03OWxDNVJzQXNiTzYtaWhZZWdiVHBSemxySE5ZcHhneW1XUE1Maw=='
https://docs.github.com/en/codespaces/managing-your-codespaces/managing-encrypted-secrets-for-your-codespaces shows how to set secrets for Codespaces.
Started work on a test:
def test_auth0_login_with_forward_url(client, settings):
forward_url = "https://www.pillarpointstewards.com/auth0-callback/"
settings.AUTH0_FORWARD_URL = forward_url
settings.AUTH0_FORWARD_SECRET = "my-secret"
response = client.get("/login/")
assert response.status_code == 302
location = response.headers["location"]
# Should redirect to auth0 with ?redirect_uri=AUTH0_FORWARD_URL with a base64 extension
assert location.startswith(
f"https://{settings.AUTH0_DOMAIN}/authorize?"
)
# Use parse_qs to extract the redirect_uri
qs = parse_qs(urllib.parse.urlparse(location).query)
redirect_uri = qs["redirect_uri"][0]
assert redirect_uri.startswith(forward_url)
base64bit = redirect_uri[len(forward_url) :]
assert base64bit
# Decode that as URL safe base64
decoded = base64.urlsafe_b64decode(base64bit.encode()).decode()
# That should be a signed message - unsign it with the secret
signer = signing.Signer(key="my-secret")
unsigned = signer.unsign(decoded)
# And that should contain our original redirect URL
assert unsigned == "http://testserver/auth0-callback/"
del settings.AUTH0_FORWARD_URL
del settings.AUTH0_FORWARD_SECRET
The following settings should now implement half of what's needed:
AUTH0_FORWARD_URL = "https://www.pillarpointstewards.com/auth0-callback/"
AUTH0_FORWARD_SECRET = "some-special-secret"
These settings, implemented in a test environment, will cause the login link to direct the user to Auth0 such that it redirects BACK to https://www.pillarpointstewards.com/auth0-callback/some-base64-signed-value
The next step is to teach /auth0-callback/...
to decode that some-base64-signed-value
, check its signature against the AUTH0_FORWARD_SECRET
setting and, if it verifies, redirect the user to that location.
Looks like /auth0-callback/...
is rejected as an invalid redirect_uri
by Auth0. But this works:
redirect_uri = forward_url + '?forward=' + signed_base64(
redirect_uri, settings.AUTH0_FORWARD_SECRET
)
That resulted in a redirect to:
https://pillarpointstewards.us.auth0.com/authorize
?response_type=code
&client_id=DLXBMP...
&redirect_uri=https%3A%2F%2Fwww.pillarpointstewards.com%2Fauth0-callback%2F%3Fforward%3DaHR0cHM6Ly9zdXBlci1kdXBlci1wYXJha2VldC01Z3c2ZzRwN2NwZ3hxLTgwMDAuYXBwLmdpdGh1Yi5kZXYvYXV0aDAtY2FsbGJhY2svOmhJaVNKYmtfcG1JVFQ1ZnRfNUVOa0loT2FSN3NOdjdYTkU5N3N2RU9faUk%3D
&scope=openid+profile+email
&state=a6eb...
Which then tried to redirect back to:
https://www.pillarpointstewards.com/auth0-callback/
?forward=aHR0cHM6Ly9zdXBlci1kdXBl...2RU9faUk%3D
&code=pIq...
&state=a6e...
Which threw a 500 error saying "state check failed, your authentication request is no longer valid" - because I haven't yet taught the production instance how to redirect back to that decoded forward URL.
I've created a secret token called PILLARPOINTSTEWARDS_AUTH0_FORWARD_SECRET
and saved it in 1Password.
I've assigned it as an environment variable in Fly like this:
fly secrets set AUTH0_FORWARD_SECRET=xxx -a pillarpointstewards
Now I'm setting it in my Codespaces secrets: https://github.com/settings/codespaces
I destroyed my Codespace and created a brand new one.
In that new one I ran this:
env | grep AUTH
Which confirmed that my AUTH0_FORWARD_SECRET
was available.
Then I ran:
AUTH0_FORWARD_URL='https://www.pillarpointstewards.com/auth0-callback/' python pillarpointstewards/manage.py runserver
I tried signing in... but I ended up signed into https://www.pillarpointstewards.com/ without being redirected back to my dev environment.
The network panel in my browser seemed to indicate that the ?forward=
parameter did not survive all the way through the auth0
redirects.
Actually no I think it's my bug - the /login/
page redirected to:
https://pillarpointstewards.us.auth0.com/authorize
?response_type=code
&client_id=DLXBMPbt...
&redirect_uri=https%3A%2F%2Fwww.pillarpointstewards.com%2Fauth0-callback%2F
&scope=openid+profile+email&state=b51...
It looks like it forgot the ?forward=
option there.
The first reason it didn't work is that clicking the "Login" link in the dev environment was hard-coded to this:
But when I did login I got an error, which was caused by this bit:
It needs the redirect_uri
AGAIN, but I need to make sure I send it the fake one from AUTH0_FORWARD_URL
Also I needed to start the dev server like this:
AUTH0_CLIENT_SECRET='014...' \
DJANGO_DEBUG=1 \
AUTH0_FORWARD_URL='https://www.pillarpointstewards.com/auth0-callback/' \
python pillarpointstewards/manage.py runserver
It needs that Auth0 secret in order to do the rest of the steps.
That worked! I had to start the server with the correct AUTH0_CLIENT_SECRET
(I had copied and pasted only part of it the first time) but I have now successfully logged in via SSO on a dev environment in GitHub Codespaces.
Originally posted by @simonw in https://github.com/natbat/pillarpointstewards/issues/73#issuecomment-1627842631