planningcenter / developers

Planning Center API docs and support
https://developer.planning.center/docs/
84 stars 8 forks source link

Anonymous API Access (like churchcenter) #1150

Closed brimworks closed 8 months ago

brimworks commented 8 months ago

Related Product Which product is this question related to? General

Describe the question How do you recommend anonymous access to planning center APIs?

More specifically, I notice that https://*.churchcenter.com/ is serving planning center content (like events) without redirecting users to the planning center oauth API. I'd like to do something similar from our website, so I'm interested in understanding how churchcenter is obtaining this content without authenticating users.

Based on the docs, I could see two ugly options: [1] Create a PAT (personal access token), perhaps as a user with less permissions? [2] Use OAuth 2.0 and have an administer initiate an access token + refresh token and periodically have our servers update this... but this gets very tricky due to the mutual exclusion requirements of refresh tokens (ideally we just perform one refresh, but there is nothing that prevents two requests for a refresh token from happening at the same time from different servers).

Overall, both of these sound like bad options, but obvious church center is doing something, so it would be great to know how we could emulate that... or I suppose I could see about initiating a cross-origin request from our app to the churchcenter APIs? I suppose that would be an unsupported solution even if I could get it to work, right?

What have you tried that worked?

N/A

What have you tried that didn't work?

N/A

Additional context

I have..

seven1m commented 8 months ago

The way this is usually handled is that you would have a server that you host your application from. A Planning Center administrator would authorize your application via OAuth and then you would store the OAuth token in your database.

Then your application can make API requests to Planning Center as if it is that admin user. This would let you give your users pseudo-anonymous access to the API via your application.

There is no way to do this purely client-side though, so you'll need a server application running to actually make the API requests.

(Church Center uses a custom API that's build purely for our own use, so there's no way for you to duplicate what Church Center is doing.)

Does this answer your question?

brimworks commented 8 months ago

Ya, that was my option [2]... the problem here is that the admin token will need to be periodically refreshed, and ensuring mutual exclusion during that refresh is difficult... and the token eventually expires after 90 days. I'd also recommend NOT using an admin user's token, since it poses a greater security risk if an attacker was somehow able to obtain it.

I've actually created a "normal" user (with no permissions) and created a PAT on that account which I'm using for the anonymous access use-case (option [1]), which is working okay.

However, it is sad that there isn't a better way to do this... especially since church center has this functionality already.

pastorhudson commented 8 months ago

I have built out option 2 at pcochef.com including several embeds. You're going to want to cache the data too since the data you need will require multiple api calls and be quite slow without caching. So that means background workers and caching server. Generally I use a try except process. I try the token and if it fails I refresh. Each worker uses the current token from my db so when one worker refreshes a token it will refresh for any other processes. Sometimes processes fail, but I'm running them every ~15 min so it will work the next time around.

This is a lot to build. I also have json endpoints for subscribers if you want to roll your own front end.

brimworks commented 7 months ago

This is a lot to build.

Agreed... which is why I'm going with option [1]. Create a user with no permissions, add a PAT, then use that in my backend API requests.

You're going to want to cache the data too since the data you need will require multiple api calls and be quite slow without caching.

Lucky for me, it seems like the calendar/v2/event_instances (when adding the include=event,tags query parameter) provides all the information I need (so no need to cache). Using where[ends_at][gt]=<now - 1 week> I can get all "future" events (I also want to display the last week of events) and if you add order=starts_at then the events are even ordered so that if there is pagination I pretty much only need to look at the first page of results... so one API call does it all for me.

pastorhudson commented 7 months ago

I use this endpoint too. Without cache you might have an issue if too many people try to load the page at the same time. You might hit a rate limit and some of the requests could fail. But hey maybe it will work for you. 👍

Even if you just cache the response every 15 min to an events.json and then load that from s3 or whatever that will fix your cors problem and potential rate limits.

brimworks commented 7 months ago

I wrote an AWS lambda with cloudfront in front of it... so cloudfront should do the caching.