Page MenuHomeSoftware Heritage

cli: Add auth command group
ClosedPublic

Authored by anlambert on Fri, Mar 20, 5:34 PM.

Details

Summary

Add a CLI tool to easily retrieve bearer tokens for Web API authentication.

$ swh auth
Usage: swh auth [OPTIONS] COMMAND [ARGS]...

  Authenticate Software Heritage users with OpenID Connect.

  This CLI tool eases the retrieval of bearer tokens to authenticate a user
  querying the Software Heritage Web API.

Options:
  --oidc-server-url TEXT  URL of OpenID Connect server (default to
                          "https://auth.softwareheritage.org/auth/")
  --realm-name TEXT       Name of the OpenID Connect authentication realm
                          (default to "SoftwareHeritage")
  --client-id TEXT        OpenID Connect client identifier in the realm
                          (default to "swh-web")
  -h, --help              Show this message and exit.

Commands:
  login    Login and create new offline OpenID Connect session.
  logout   Logout from an offline OpenID Connect session.
  refresh  Refresh an offline OpenID Connect session.

The proposed workflow to authenticate API calls is the following.

When a user has an account on the swh Identity Provider server (Keycloak), he can
login using this tool and get its authentication tokens to send in HTTP headers
when querying the Web API.

$ swh auth login johndoe | jq
Password: 
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJPSnhVQ0p0TmJQT0NOUGFNNmc3ZU1zY2pqTXhoem9vNGxZaFhsa1c2TWhBIn0.eyJqdGkiOiI3NjMzNTAzZi04NGFjLTRmZTMtOGRmNS01ZTQ4MTAxZjAwMmIiLCJleHAiOjE1ODQ3MjE3NTAsIm5iZiI6MCwiaWF0IjoxNTg0NzIxMTUwLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvU29mdHdhcmVIZXJpdGFnZSIsImF1ZCI6WyJzd2gtd2ViIiwiYWNjb3VudCJdLCJzdWIiOiJmZWFjZDM0NC1iNDY4LTRhNjUtYTIzNi0xNGY2MWU2YjcyMDAiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJzd2gtd2ViIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiYzE0ZTFiN2ItODI2My00ODUyLWJkMWMtYWRjN2JjMTJhMTM2IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsic3doLXdlYiI6eyJyb2xlcyI6WyJ0aHJvdHRsaW5nLWV4ZW1wdGVkIl19LCJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIG9mZmxpbmVfYWNjZXNzIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiSm9obiBEb2UiLCJncm91cHMiOlsiL3BhcnRuZXJzIl0sInByZWZlcnJlZF91c2VybmFtZSI6ImpvaG5kb2UiLCJnaXZlbl9uYW1lIjoiSm9obiIsImZhbWlseV9uYW1lIjoiRG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSJ9.hFeLjlxkj3OzotJDwbuIZrSePmJdxRnxMTAla_nhgTnKdmY4TcMxyA2gaAG3ig7sSKbO3s86XCsn4B943TygLgS_YXYkpulQdk3bwUTu4zISE2zBtB2NFUyFie05Zxq3Z6zrlAw9TAApq4Ig0VnS5axpYdiQaXiTif4bLPZzxVRJ5NTHZJjjXjv7XUXhbH62Su0xd7FdUaxWMvCFrEY3ONiFxZ9v8n_jZNYt_1A1qPuR-LvPhytu86xOq7IImKfyrRcfAfYHUDmA4S5XduE2viIDNA_0p2bUuYwQwhjfHbv7IASAGquW6DqA6hAJp4dafnPyiRUDdlwtWVypo-ttMw",
  "expires_in": 600,
  "refresh_expires_in": 0,
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJmNjMzMDE5MS01YTU4LTQxMDAtOGIzYS00ZDdlM2U1NjA3MTgifQ.eyJqdGkiOiI2ZjU5ZWFkNS05OWIzLTRiMWUtYWM3My0xNTRmYzc2NmNjYWIiLCJleHAiOjAsIm5iZiI6MCwiaWF0IjoxNTg0NzIxMTUwLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvU29mdHdhcmVIZXJpdGFnZSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hdXRoL3JlYWxtcy9Tb2Z0d2FyZUhlcml0YWdlIiwic3ViIjoiZmVhY2QzNDQtYjQ2OC00YTY1LWEyMzYtMTRmNjFlNmI3MjAwIiwidHlwIjoiT2ZmbGluZSIsImF6cCI6InN3aC13ZWIiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiJjMTRlMWI3Yi04MjYzLTQ4NTItYmQxYy1hZGM3YmMxMmExMzYiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7InN3aC13ZWIiOnsicm9sZXMiOlsidGhyb3R0bGluZy1leGVtcHRlZCJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgZW1haWwgcHJvZmlsZSBvZmZsaW5lX2FjY2VzcyJ9.h8AxJU1_ouNrwnz6NKKx8AKBu2UbtOu1riP4Jil7XWU",
  "token_type": "bearer",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJPSnhVQ0p0TmJQT0NOUGFNNmc3ZU1zY2pqTXhoem9vNGxZaFhsa1c2TWhBIn0.eyJqdGkiOiIwZGIwMmM5Zi0yZGJlLTRiMjgtOTYzNS1lMGVhNjliMWM3NjEiLCJleHAiOjE1ODQ3MjE3NTAsIm5iZiI6MCwiaWF0IjoxNTg0NzIxMTUwLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvU29mdHdhcmVIZXJpdGFnZSIsImF1ZCI6InN3aC13ZWIiLCJzdWIiOiJmZWFjZDM0NC1iNDY4LTRhNjUtYTIzNi0xNGY2MWU2YjcyMDAiLCJ0eXAiOiJJRCIsImF6cCI6InN3aC13ZWIiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiJjMTRlMWI3Yi04MjYzLTQ4NTItYmQxYy1hZGM3YmMxMmExMzYiLCJhY3IiOiIxIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiSm9obiBEb2UiLCJncm91cHMiOlsiL3BhcnRuZXJzIl0sInByZWZlcnJlZF91c2VybmFtZSI6ImpvaG5kb2UiLCJnaXZlbl9uYW1lIjoiSm9obiIsImZhbWlseV9uYW1lIjoiRG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSJ9.i5QcmvmAYv4m26mrMQZi_FUuKNqg3I9yN8IdJ2nGKS-1q7jjJkjfVtOut6cdQ_V4UY7X9U7p4bXN3TRReOKn8dHsSlPwQpfzP9r6BimPAOFMS5yw-QAgOiLWFPtD8jlFwNCKNC5mT5iftTv-e9nEAGGgrFbgmsieIhVJzYb4sn8UvjX2YikHn2emYnzobrv_7hTHYOr6JXouzNKiziNOdEB1K_H4zAngfidXexZoQa872x_z0XpEYwZmWYc1PScGsAwc1qSQLz014StytnNiKcOjbUX8fQ64xT7xkf3QPiaye0usVzCNmh3kURfF3aW1Kmhax-AUKDItzO6fvHWtPQ",
  "not-before-policy": 1584551170,
  "session_state": "c14e1b7b-8263-4852-bd1c-adc7bc12a136",
  "scope": "openid email profile offline_access"
}

The access token must be sent in HTTP headers to authenticate requests to Web API.
It has a short living period (usually 10 minutes) and must be renewed regularly.

To renew an access token, the refresh token has to be used by sending it to the Keycloak
server in a refresh request. This token has a longer living period and can be
used at anytime to get a new access token without the need to login again.

The refresh operation will be automatically handled if you use the swh.web.client.WebAPIClient
class (diff incoming for that feature).

For users not working with Python, they can get a new access token through this command:

$ swh auth refresh $TOKEN | jq
"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJPSnhVQ0p0TmJQT0NOUGFNNmc3ZU1zY2pqTXhoem9vNGxZaFhsa1c2TWhBIn0.eyJqdGkiOiI1ZTJiZWJmMS1hNDhhLTRlYjAtODBjNi01MWM3MTlhOWNjZjUiLCJleHAiOjE1ODQ3MjIyMDksIm5iZiI6MCwiaWF0IjoxNTg0NzIxNjA5LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvU29mdHdhcmVIZXJpdGFnZSIsImF1ZCI6WyJzd2gtd2ViIiwiYWNjb3VudCJdLCJzdWIiOiJmZWFjZDM0NC1iNDY4LTRhNjUtYTIzNi0xNGY2MWU2YjcyMDAiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJzd2gtd2ViIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiYzE0ZTFiN2ItODI2My00ODUyLWJkMWMtYWRjN2JjMTJhMTM2IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsic3doLXdlYiI6eyJyb2xlcyI6WyJ0aHJvdHRsaW5nLWV4ZW1wdGVkIl19LCJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIG9mZmxpbmVfYWNjZXNzIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiSm9obiBEb2UiLCJncm91cHMiOlsiL3BhcnRuZXJzIl0sInByZWZlcnJlZF91c2VybmFtZSI6ImpvaG5kb2UiLCJnaXZlbl9uYW1lIjoiSm9obiIsImZhbWlseV9uYW1lIjoiRG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSJ9.J8BTceH3VrfDLw677CLys7cNZgiyaRC0_ewtegn_thdFzv1CwDULKnTIZHb-o_nBXhpA-hEaMUnu-tsE3yleCqMuKLjBq6c9kt2bVGvyR3-aIcbBb5gX6KzJPxPSBfvz-mQzjpuQMfswPMrYkDm--fQa3KgwuHc5CDs15rPSINKH7YZLkVbi4rIQ--7DhbqM3F4NPIUrTIAb7pSAIe2N9XzSZHc0ZbWbq7-NkxjOL-fOeI8oqPbQkFDIq7vzTcGYiiI9VJoIIxTHtkTYJHv6RdU3Yj2u0-im7P-_8em0waaHy1zAq2SBzXSKpadlQ22k6yiBrcQ6gNov0w896nVcPA"

It is also possible to logout from the authenticated session which invalidates all previously
emitted tokens.

$ swh auth logout $TOKEN
Successfully logged out from OpenID Connect session

Diff Detail

Repository
rDWCLI Web client
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.

Event Timeline

anlambert created this revision.Fri, Mar 20, 5:34 PM
anlambert updated this revision to Diff 10189.Fri, Mar 20, 5:36 PM

Update: add missing click depdendency and update commit message

anlambert updated this revision to Diff 10190.Fri, Mar 20, 5:38 PM

Update: add missing pytest-mock dependency

vlorentz requested changes to this revision.Fri, Mar 20, 6:08 PM
vlorentz added a subscriber: vlorentz.

Could you write this Diff's description in the README?

swh/web/client/auth.py
76

why doesn't this one request offline_access?

93

same

swh/web/client/cli.py
18

The group should be named "authentication", as it's not a single action

40

You should first use ctx.ensure_object()

45

Why the session- prefix?

48

if you want the session- prefix, then the function name should be session_login

70

print(json.dumps(resp_json, indent=4, sort_keys=True)) to be developer-readable

72

Hiding the traceback makes debugging harder :/

And we should exit with non-0 in case of error.

89

same

92

same

94

same

112

same

swh/web/client/tests/test_cli.py
27–28

You can use the input= argument of runner.invoke instead

57

needs a test of failure for each command

This revision now requires changes to proceed.Fri, Mar 20, 6:08 PM
anlambert marked 13 inline comments as done.Fri, Mar 20, 6:33 PM

Thanks for the review !

Could you write this Diff's description in the README?

I will create a dedicated page in the module documentation.

swh/web/client/auth.py
76

The offline_access is only needed when logging in. It informs Keycloak that an offline token should be generated for the session. This is a special type of refresh token with longer living period.

swh/web/client/cli.py
18

ack

40

ack

45

To explicit the fact that it opens an authenticated session on OIDC server.

But I agree shorter is better and the doc give the details, I will remove the prefix.

70

ack

72

Can I just remove the try/except block then ?

swh/web/client/tests/test_cli.py
27–28

nice, thanks

57

ack

vlorentz added inline comments.Fri, Mar 20, 6:55 PM
swh/web/client/cli.py
72

I guess. Or catch specific expected errors, and let everything raise to the toplevel

anlambert marked 7 inline comments as done.Fri, Mar 20, 6:57 PM
anlambert added inline comments.
swh/web/client/cli.py
72

Ok will remove the blocks then.

anlambert added inline comments.Fri, Mar 20, 7:22 PM
swh/web/client/tests/test_cli.py
27–28

Unfortunately this does not seem to play well with getpass, providing input='password\n' still makes the prompt appear. Let's keep this mock then.

anlambert updated this revision to Diff 10193.Fri, Mar 20, 7:26 PM

Update: Rebase and address @vlorentz comments, still the documentation to write

anlambert edited the summary of this revision. (Show Details)Fri, Mar 20, 7:27 PM
anlambert edited the summary of this revision. (Show Details)
anlambert updated this revision to Diff 10194.Fri, Mar 20, 7:36 PM

Update: restore default oidc-server-url value

anlambert retitled this revision from cli: Add authenticate command groups to cli: Add authentication command groups.Fri, Mar 20, 7:40 PM
vlorentz accepted this revision.Fri, Mar 20, 7:55 PM
vlorentz added inline comments.
swh/web/client/tests/test_cli.py
27–28

indeed, getpass, reads from the tty

41

can remove this.

This revision is now accepted and ready to land.Fri, Mar 20, 7:55 PM

Don't forget to add the doc!

anlambert updated this revision to Diff 10204.Mon, Mar 23, 12:40 PM

Update: Rebase and add CLI tool documentation in a new Authentication section located in docs/index.rst

nice.

swh/web/client/cli.py
43

you can remove that pass ;)

you've got a free pass for that one :D

90

maybe add a output_json function (or something) to avoid the repetition?

I saw this at least thrice already.

anlambert added inline comments.Mon, Mar 23, 2:51 PM
swh/web/client/cli.py
43

Whoopsie, thanks for spotting !

90

Agreed !

anlambert updated this revision to Diff 10219.Mon, Mar 23, 6:10 PM

Update: Address @ardumont comments

ardumont accepted this revision.Mon, Mar 23, 6:12 PM
zack added a subscriber: zack.Tue, Mar 24, 9:28 AM

the doc LGTM, but swh authentication feels like a mouthful, would it be possible to use swh auth instead? (which will also work for both "authorization", that I suspect is something we will also put behind this at some point)

In D2861#69178, @zack wrote:

the doc LGTM, but swh authentication feels like a mouthful, would it be possible to use swh auth instead? (which will also work for both "authorization", that I suspect is something we will also put behind this at some point)

Ack, I will update accordingly.

anlambert updated this revision to Diff 10229.Tue, Mar 24, 11:56 AM

Update: Rename command group from authentication to auth

anlambert retitled this revision from cli: Add authentication command groups to cli: Add auth command group.Tue, Mar 24, 11:57 AM
anlambert edited the summary of this revision. (Show Details)
This revision was automatically updated to reflect the committed changes.