Skip to content

POST: storing and using httr2 cache tokens on CI#1173

Open
drmowinckels wants to merge 21 commits intoropensci:mainfrom
drmowinckels:main
Open

POST: storing and using httr2 cache tokens on CI#1173
drmowinckels wants to merge 21 commits intoropensci:mainfrom
drmowinckels:main

Conversation

@drmowinckels
Copy link
Copy Markdown
Contributor

Maëlle and I describe how we set up meetupr package for CI/CD compatibility. Intended to be cross posted to R-Ladies blog, awaiting their blog team's decision.

@drmowinckels
Copy link
Copy Markdown
Contributor Author

@maelle first draft ready for your feedback!

Copy link
Copy Markdown
Member

@maelle maelle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!! I made some comments. I wonder whether a diagram would help. And a link to a real GHA example?

Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
@yabellini yabellini added the blog post Blog posts to be published when merged label Dec 3, 2025
@yabellini
Copy link
Copy Markdown
Member

Hi @drmowinckels and @maelle, no rush, just let us know when this is ready to review for our editorial team or if you need any help from us to move forward.

@drmowinckels
Copy link
Copy Markdown
Contributor Author

I'll get to this next week I think. We ended up with a completely different approach in the end, so needs a pretty big rewrite.

@yabellini
Copy link
Copy Markdown
Member

I'll get to this next week I think. We ended up with a completely different approach in the end, so needs a pretty big rewrite.

Thanks for letting me know. No pressure and no rush. I was just going through our pending posts and want to get a sense of timing to schedule publications.

@drmowinckels
Copy link
Copy Markdown
Contributor Author

@maelle did some updates, added what you suggested :)

@drmowinckels drmowinckels requested a review from maelle March 18, 2026 15:58
Copy link
Copy Markdown
Member

@maelle maelle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay!! I find it very clear especially knowing the pain and confusion that the Meetup API and OAuth create.

title: "Cache Me If You Can – httr2 OAuth Token Management for CI/CD"
author:
- Athanasia Mo Mowinckel
- Maëlle Salmon
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether I should be an editor instead, given what my input was.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I leave that to you, but I think of you as co-auther if that counts for something.

Comment thread content/blog/2025-10-30-httr2-cache/index.md
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated

## Understanding httr2's OAuth flow

httr2's `req_oauth_auth_code()` handles [OAuth2](https://blog.r-hub.io/2021/01/25/oauth-2.0/) authentication for local development:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I obviously like that R-hub post but maybe link to https://httr2.r-lib.org/articles/oauth.html instead/as well?

Comment thread content/blog/2025-10-30-httr2-cache/index.md
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
drmowinckels and others added 9 commits April 3, 2026 19:59
Co-authored-by: Maëlle Salmon <maelle.salmon@yahoo.se>
Co-authored-by: Maëlle Salmon <maelle.salmon@yahoo.se>
Co-authored-by: Maëlle Salmon <maelle.salmon@yahoo.se>
Co-authored-by: Maëlle Salmon <maelle.salmon@yahoo.se>
Co-authored-by: Maëlle Salmon <maelle.salmon@yahoo.se>
Co-authored-by: Maëlle Salmon <maelle.salmon@yahoo.se>
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
title: "Cache Me If You Can – httr2 OAuth Token Management for CI/CD"
author:
- Athanasia Mo Mowinckel
- Maëlle Salmon
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I leave that to you, but I think of you as co-auther if that counts for something.

Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Comment thread content/blog/2025-10-30-httr2-cache/index.md Outdated
Copy link
Copy Markdown
Contributor Author

@drmowinckels drmowinckels left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor changes

@drmowinckels drmowinckels marked this pull request as ready for review April 4, 2026 10:22
@drmowinckels
Copy link
Copy Markdown
Contributor Author

To whomever will be editor, we want to crosspost this to the R-Ladies blog, but have not set up the post there yet. I thought it would be best to get it ready here, and then set a publishing date together with the @rladies/blog team

@yabellini
Copy link
Copy Markdown
Member

@drmowinckels, that sounds good. Is this ready for editorial review? If yes, then @steffilazerte will be the editor 😸

@drmowinckels
Copy link
Copy Markdown
Contributor Author

ready for review @steffilazerte .

Copy link
Copy Markdown
Member

@steffilazerte steffilazerte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @drmowinckels!

This is a great post and a fascinating idea! I'm already thinking about how to incorporate it into my promoutils package so I don't have to deal with the httr2 token storage, thanks!

Overall, this is a very clear and well written post. I have one large suggestion regarding moving the "What we tried first" section to earlier, and most of the other comments are small wording tweaks, or add a bit of context to remind the reader what you're talking about.

For moving the "What we tried first" section, see my comments for specifics, but broadly, my suggestion is to:

  • Move it to right before "How it works"
  • Rename to "Why encrypted tokens?"
  • Add the first paragraph of the "How it works" to the end of this section so the two sections flow from one into the other.

These are just suggestions, however, so pick and chose what works best for you!

We have a couple of options for dates, depending what @maelle thinks and how long you need to make any edits.

I'm away until April 24th, so we could publish April 28th, when I'm back, or if you're ready earlier, @maelle could publish on the 21st (or anytime next week that sounds good). Just let me know what works best for you (and @maelle, if that works for you!)

  • post follows Content Guidelines
  • post follows Style Guide
  • title is in Title Case
  • publication date is ok
  • slug is ok
  • alternative text of images is informative
  • author metadata is provided with correct folder name for each language
  • html not included in pull request of Rmd post
  • I ran roblog::ro_lint_md() on index.md
  • I ran roblog::ro_check_urls() on index.md
  • I ran a spell-check on index.md
  • YAML subject tags are ok ("tech notes" for tech notes; "community" for non-staff non-editor)
  • YAML field 'editor' is filled with your name
  • YAML field 'translator' is filled as needed
  • YAML field 'interviewee' is filled as needed
  • YAML field 'preface' is present if necessary
  • YAML field 'params.doi' is filled with a new DOI

2. _Encrypted token file_ — for regular OAuth apps in CI
3. _Interactive OAuth_ — the standard browser flow for local development

![Flowchart showing the authentication decision tree. Starting from an API request, the package first checks if JWT credentials are available. If yes, it uses JWT bearer token auth. If not, it checks for an encrypted token file. If found, it decrypts and uses that token. If neither is available, it falls back to the interactive browser OAuth flow.](auth-decision-flow.png "Authentication decision flow — the package tries each method in order and uses the first one available.")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the "\n" be New Lines? Looks like that didn't work in the figure...

Comment on lines +82 to +87
req <- req |>
httr2::req_oauth_bearer_jwt(
client = meetupr_client(...),
claim = httr2::jwt_claim(...)
))
return(req)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to match the rest

Suggested change
req <- req |>
httr2::req_oauth_bearer_jwt(
client = meetupr_client(...),
claim = httr2::jwt_claim(...)
))
return(req)
req <- req |>
httr2::req_oauth_bearer_jwt(
client = meetupr_client(...),
claim = httr2::jwt_claim(...)
))
return(req)

Our first approach — suggested by [Noam Ross](https://www.noamross.net/) on the rOpenSci Slack — was to base64-encode the raw httr2 cache file and store it as a CI environment variable.
This worked, but had drawbacks: environment variables have size limits on some CI providers, you had to store both the token content _and_ httr2's hash-based filename as separate secrets, and there was no natural way to write back a rotated token.

It is a bit surprising!
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was it really surprising? The next sentence seems to argue that this is expected? Perhaps either remove the "It is a bit surprising!" Statement, or tweak it to something like "It was a bit surprising, but made sense once we thought about it a bit more." (or similar).

Instead of the encrypted file going stale after the access token expires, it stays current.
The caveat being, that every token refresh needs to be committed and pushed to the repo by the CI.

### What we tried first
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this section should go right before you describe the encryption part (i.e. before the 'How it works' part). It seems to break the flow a bit here and seems out of order to talk about what you did first after you talk about what you did second 😁

If you do that, consider remaming this section to "Why encrypted tokens?"

Comment on lines +203 to +206
The encrypted file approach is simpler for users — commit one file, set one secret — and gives CI a place to write back the refreshed token.
By using an encrypted file in the repository, you sidestep this limitation.
The file acts as a renewable credential that CI can update and push, while the decryption password remains protected as a CI secret.
This pattern is not common in most documentation, but it works well for R packages and similar use cases.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If moving this section to earlier, consider adding a bit of the "How it works" introduction here so it flows directly into that section.

Suggested change
The encrypted file approach is simpler for users — commit one file, set one secret — and gives CI a place to write back the refreshed token.
By using an encrypted file in the repository, you sidestep this limitation.
The file acts as a renewable credential that CI can update and push, while the decryption password remains protected as a CI secret.
This pattern is not common in most documentation, but it works well for R packages and similar use cases.
So we thought of another approach, using encrypted token files. The idea is simple: authenticate locally once, then encrypt and commit the token so CI can use it. Only the decryption password goes into CI secrets — the encrypted file itself lives in your repository.
This is simpler for users — commit one file, set one secret — and gives CI a place to write back the refreshed token.
By using an encrypted file in the repository, you sidestep the limitations of CI environmental variables.
The file acts as a renewable credential that CI can update and push, while the decryption password remains protected as a CI secret.
This pattern is not common in most documentation, but it works well for R packages and similar use cases.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again I don't see this referenced... I think it can be used for the social media card, but then you need the YAML key socialImg

JWT is straightforward — if you have the credentials, httr2 handles the rest.
The interesting part is the encrypted token path.
The one we wanted to use in our CI workflows!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest moving the "What we tried first" section here, and renaming it "Why encrypted tokens?" (see my other comments below)

@@ -0,0 +1,328 @@
---
title: "Cache Me If You Can – httr2 OAuth Token Management for CI/CD"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great title!

author:
- Athanasia Mo Mowinckel
- Maëlle Salmon
date: '2026-03-30'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember to adjust the date here and in the folder name

output: hugodown::md_document
tags:
- oAuth
- tech notes
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- tech notes
- tech notes
- CI
- API

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blog post Blog posts to be published when merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants