-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Integrating external identity providers, and setting up access-based controls. #24
Comments
I think that initialization should be automatic, by injecting AuthProvider instance in the
And for unsupported "drivers", there will be a custom option in the API (which will require more code on the client and server, but more flexibility from day 1). Just a simple suggestion. What do you think? |
Hey @drantunes! I agree this would be great, but it creates a few challenges we wanted to avoid:
I hear you, though, and we'll think on how we can simplify the interface even more! Thanks for your feedback 🤩 |
I believe this is the point: make the API very simple for greater acceptance by beginner and intermediate devs. I already imagined these problems, as there are some SDKs that require the client to have their dependency + the third dependency wrapper (e.g. celest_firebase)... this really creates problems |
Just a subjective comment regarding "Applying access control to multiple Celest Functions":
But it could absolutely be possible that I am a minority and other users would use vastly these features above! This is the way I generally structure my cloud functions is the following:
so for instance export const fetchHomeTours = fetchHomeToursF;
export const fetchPublicTour = fetchPublicTourF;
export const fetchPrivateTour = fetchPrivateTourF;
... and then export const fetchHomeToursF = functions
.region(BusinessConstants.deployRegion)
.https.onRequest(async (req, res) => {
return await customFunctionStructureWithErrorHandling({
req,
res,
requiresAuthentication: false, // <- whether requires auth or not
requiresOrigin: true,
functionBody: async () => {
const client = await getInitializedClient();
const result = await client.manager.findAll(HomeTour);
const body = JSON.stringify({ data: result }, null, 2);
await client.destroy();
res.status(200).send(body);
},
});
}); |
Got it - thank you for sharing this feedback @mariopepe. We were trying to come up with a consistent pattern for all definitions of authorization rules, and we felt the annotations were lightweight and easy enough for developers to scan their code and understand how authorization is applied on it for their Celest backend. We will keep this in mind in case we hear the same feedback from more customers! |
Hi, how can I bypass or simulate the authentication, when I'm running my integrations tests? For example, suppose I want to test a chat app. I want my test to signUp users Mary and John. Then, login as Mary, and I want to test that when Mary sends a message to John, then John gets the message. In Given/When/Then format, my test would be something like this: // Given Mary and John are users AND Mary is logged in.
var mary = User(name: "Mary", ...);
var john = User(name: "John"...);
signUp(mary, ...);
signUp(john, ...);
login(mary);
// When Mary sends John a message.
sendMessage(from: mary, to: john, text: "hello");
logout();
// Then when John logs in, he gets Mary's message.
login(john);
var msg = getAllMessages();
expect(msg.from, mary);
expect(msg.text, "hello");
logout(); |
Great question, @marcglasberg! With Celest Auth (which we'll dive into in a future RFC), the above will be possible. This is because our Auth solution will allow a local iteration playground in the same way Celest Functions currently does. The types of simulations you're describing where you can impersonate users and impersonate roles will be possible using the generated client and the local environment created by Food-for-thought, for now. At the moment, we're not planning to expose this for external identity providers, though let me know if that would be a blocker for you. |
I intend to use Celest Auth only. So, not a blocker. |
This RFC is intended to collect feedback on Celest’s developer experience around integrating external identity providers (e.g. Firebase Auth, Supabase Auth, Custom OIDC, etc.), and applying authorization rules to your Celest backend. We are sharing it as early as we can to get your feedback.
We will cover 3 key topics:
Starting a new Celest project
You first need to create a new Celest project, follow our getting started guide to learn more.
Integrating your authentication provider
In the future, Celest will have an authentication solution to help you create and authenticate your users. However, even without that, you will have the ability to integrate existing identity providers that you currently use in your Flutter projects to authenticate users in your Celest backend.
To set up your authentication provider in your Celest project, you will need to go to the Celest folder in your Flutter project that was created for you by the Celest CLI. Create a folder called
auth
and then create aproviders
folder inside of it. Then, you will need to create a new file for each identity provider you want to use with your Celest backend. The following is an example of how you would define Firebase Auth as your identity provider.Note
Identity providers you use with Celest must be OIDC-compliant
That is all the set up you need in your backend. Now, you need to go to your Flutter app, and add the following code to the entry point of your app, which is typically the
main.dart
file.In this code snippet, we have initialized Firebase and Celest in the Flutter app, initialized a stream that will always grab the latest firebase idToken on the device, and used
grantPermissions
to pass the Firebase token stream to Celest.Now, you are ready to start adding auth guards to your Celest Functions.
Controlling access to Celest Functions
Note
Access to Celest Functions is always deny-by-default.
You have the following options to control access to Celest Functions:
Public Access
To enable public access to your Celest Function, navigate to your
<flutter_app>/celest/functions/<my_api>.dart
file, and add the@public
directive on top of it.Authenticated access
To enable only authenticated users to access your Celest Function, navigate to your
<flutter_app>/celest/functions/<my_api>.dart
file, and add the@authenticated
directive on top of it.Role-based access
Role-based access controls enable you to have more granular authorization for your users by mapping their permissions to roles you define. This is important to help you create functions that need to be accessed only by specific groups of users such as admins.
To set up role-based access controls, you first need to define the roles that you want to grant to your users by navigating to the
<flutter_app>/celest/auth/
folder, and creating aroles.dart
file. There, define as many roles as your application needs.In order for your users to be assigned these roles, you will need to define a hook which maps the validated ID or access token into the roles you’ve defined.
Imagine if the structure of the Firebase idToken looks as shown in the following example. The
customClaims
key is where the roles or special access permission information will typically be available.You will need to add a definition for the
onGrantPermissions
hook for your identity provider you have previously created. Navigate to the<flutter_app>/celest/auth/providers/<provider.dart>
file. The following code snippet has an example of parsing out the idToken from Firebase to derive custom claims, which contains the access permissions or roles a specific user has.This code snippet shows an example of assigning the role of
admin
if the custom claim ofaccessLevel
is available and set toadmin
on the idToken from Firebase. Depending on the identity provider, custom claims can be a part of either the idToken or access token. For example, Supabase provides custom claims in the access tokens. Celest supports using either for extracting claims.Then, add the permissions on the Celest Functions you want to grant the
admin
role access to. The following example shows you how to set thegreetAdmin
function access to be allowed only for users granted theadmin
role.With Celest, you can set up as many roles and grant access permission policies to your Celest Functions are required by your application use cases.
Applying access control to multiple Celest Functions
if you have multiple Celest Functions in your API file, and you want to add an Auth guard or access policy for all of them, you can do that by annotating your desired permissions using the
library
directive at the top of the API file.Using role-based access controls in your Flutter app
You can also use the roles that have been applied by your Celest backend in your Flutter app. This means that you can centralize all your access control policies in your Celest backend definition, and then use them to control rendering the UI in your Flutter app.
The following code example shows you how to render your UI conditionally if your user is an admin. Using
celest.auth.roles
function on your frontend returns a list of roles that the signed in user has been assigned.Next steps
If there’s anything we’ve left out or things you would like to see discussed, please us know! We are also working on adding a public roadmap, creating additional RFCs for event-driven workflows, and launching cloud deployments and management.
Thank you for coming on this journey with us 💙 We are so excited to bring you these features and more very soon! 🚀
If you haven’t already, sign up for our waitlist to get the latest updates on our progress and follow us on Twitter/X where we share more insights and behind-the-scenes snippets.
The text was updated successfully, but these errors were encountered: