Skip to content

Commit

Permalink
feat: Celest Auth
Browse files Browse the repository at this point in the history
- Adds `package:celest_auth` with initial email OTP support
- Adds an example of using `celest_auth` within `packages/celest_auth/example`.
- Adds `@authenticated`, `@public`, and `@Context.user` annotations to `package:celest`
  • Loading branch information
dnys1 committed Mar 9, 2024
1 parent 2a57388 commit a20fd2e
Show file tree
Hide file tree
Showing 182 changed files with 6,233 additions and 14 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/celest_auth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: celest_auth
on:
pull_request:
paths:
- ".github/workflows/celest_auth.yaml"
- "packages/celest_auth/**"

# Prevent duplicate runs due to Graphite
# https://graphite.dev/docs/troubleshooting#why-are-my-actions-running-twice
concurrency:
group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }}-${{ github.ref == 'refs/heads/main' && github.sha || ''}}
cancel-in-progress: true

jobs:
build:
runs-on: macos-latest-xlarge
timeout-minutes: 15
steps:
- name: Git Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # 4.1.1
- name: Setup Flutter
uses: subosito/flutter-action@62f096cacda5168a3bd7b95793373be14fa4fbaf # 2.13.0
with:
channel: stable
cache: true
- name: Get Packages
working-directory: packages/celest_auth
run: dart pub get && dart pub get --directory=example/celest
- name: Analyze
working-directory: packages/celest_auth
run: dart analyze --fatal-infos --fatal-warnings
- name: Format
working-directory: packages/celest_auth
run: dart format --set-exit-if-changed .
- name: Test (Example)
working-directory: packages/celest_auth/example/celest
run: dart test
- name: Build Example (macOS)
working-directory: packages/celest_auth/example
run: flutter build macos
- name: Build Example (Web)
working-directory: packages/celest_auth/example
run: flutter build web
1 change: 1 addition & 0 deletions examples/auth
8 changes: 8 additions & 0 deletions packages/celest/lib/celest.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@ library celest;

export 'package:celest_core/celest_core.dart';

/// Auth
export 'src/auth/auth.dart';
export 'src/auth/auth_provider.dart';

/// Config
export 'src/config/env.dart';

/// Core
export 'src/core/cloud_widget.dart';
export 'src/core/context.dart';
export 'src/core/project.dart';

/// Functions
export 'src/functions/cloud_api.dart';
export 'src/functions/cloud_function.dart';

/// Grants
export 'src/grants/grants.dart';
10 changes: 5 additions & 5 deletions packages/celest/lib/src/auth/auth_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
/// An authentication provider which can be used to sign in to Celest.
///
/// Currently, Celest supports the following authentication methods:
/// - [AuthProvider.google] Sign in with Google
/// - [AuthProvider.email] Email sign-in with OTP codes.
/// {@endtemplate}
sealed class AuthProvider {
const AuthProvider();

/// A provider which enables Sign in with Google.
const factory AuthProvider.google() = _GoogleAuthProvider;
/// A provider which enables email sign-in with OTP codes.
const factory AuthProvider.email() = _EmailAuthProvider;
}

final class _GoogleAuthProvider extends AuthProvider {
const _GoogleAuthProvider();
final class _EmailAuthProvider extends AuthProvider {
const _EmailAuthProvider();
}
36 changes: 36 additions & 0 deletions packages/celest/lib/src/core/context.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:celest/celest.dart';

/// The context of a [CloudFunction] invocation.
abstract final class Context {
/// A context reference to the [User] invoking a [CloudFunction].
///
/// ## Example
///
/// To inject a user into an `@authenticated` function:
///
/// ```dart
/// @authenticated
/// Future<void> sayHello({
/// @Context.user required User user,
/// }) async {
/// print('Hello, ${user.displayName}!');
/// }
/// ```
///
/// If a user is injected to a `@public` or private function, then the
/// user parameter must be nullable:
///
/// ```dart
/// @public
/// Future<void> sayHello({
/// @Context.user User? user,
/// }) async {
/// print('Hello, ${user?.displayName ?? 'stranger'}!');
/// }
/// ```
static const user = _UserContext();
}

final class _UserContext implements Context {
const _UserContext();
}
6 changes: 0 additions & 6 deletions packages/celest/lib/src/core/project.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,8 @@ class Project implements CloudWidget {
/// {@macro celest.core.project}
const Project({
required this.name,
this.logoUrl,
});

/// The name of the project as its identified in your Celest backend.
final String name;

/// The hosted URL of your project's logo.
///
/// This is used by widgets like [Auth] to craft a custom login page.
final String? logoUrl;
}
61 changes: 61 additions & 0 deletions packages/celest/lib/src/grants/grants.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'package:celest/src/core/entity.dart';

/// A grant which provides access to all authenticated users.
///
/// ## Example
///
/// ```dart
/// import 'package:celest/celest.dart';
///
/// @authenticated
/// Future<void> sayHello() async {
/// print('User is authenticated!');
/// }
/// ```
const authenticated = _Authenticated();

/// A grant which provides access to everyone.
///
/// **NOTE**: Using this grant on a function or library will make it accessible
/// to the public internet. Anyone will be able to call the functions.
///
/// ## Example
///
/// ```dart
/// import 'package:celest/celest.dart';
///
/// @public
/// Future<void> sayHello() async {
/// print('Hello, stranger!');
/// }
/// ```
const public = _Public();

final class _Role implements Entity {
const _Role({
required this.name,
});

static const _Role authenticated = _Role(name: r'$authenticated');

final String name;
}

/// An assignment which grants a set of permissions to a specific group of
/// [Entity]s.
final class _Grant {
const _Grant({
required this.to,
});

/// The group of [Entity] which are granted access.
final List<Entity> to;
}

final class _Authenticated extends _Grant {
const _Authenticated() : super(to: const [_Role.authenticated]);
}

final class _Public {
const _Public();
}
14 changes: 13 additions & 1 deletion packages/celest/lib/src/runtime/serve.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,22 @@ abstract base class CloudFunctionTarget {
init();
}

static const _contextHeaderPrefix = 'X-Celest-Context-';
static final _contextHeaderMatcher = RegExp(
_contextHeaderPrefix,
caseSensitive: false,
);

Future<Response> _handler(Request request) async {
final bodyJson = await request.decodeJson();
final response = await runZoned(
() => handle(bodyJson),
() => handle({
for (final MapEntry(:key, :value) in request.headers.entries)
if (key.startsWith(_contextHeaderMatcher))
'\$${key.substring(_contextHeaderPrefix.length).toLowerCase()}':
value,
...bodyJson,
}),
zoneSpecification: ZoneSpecification(
print: (self, parent, zone, message) {
parent.print(zone, '[$name] $message');
Expand Down
7 changes: 7 additions & 0 deletions packages/celest_auth/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
3 changes: 3 additions & 0 deletions packages/celest_auth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 0.3.0

- Initial version.
1 change: 1 addition & 0 deletions packages/celest_auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Celest Auth
14 changes: 14 additions & 0 deletions packages/celest_auth/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
include: package:lints/recommended.yaml

analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true
errors:
# To prevent issues publishing.
depend_on_referenced_packages: error
public_member_api_docs: ignore # TODO
implementation_imports: ignore # TODO
exclude:
- "**/*.ffi.dart"
43 changes: 43 additions & 0 deletions packages/celest_auth/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
45 changes: 45 additions & 0 deletions packages/celest_auth/example/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "7482962148e8d758338d8a28f589f317e1e42ba4"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: android
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: ios
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: linux
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: macos
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: web
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: windows
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
3 changes: 3 additions & 0 deletions packages/celest_auth/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# celest_auth_example

A new Flutter project.
1 change: 1 addition & 0 deletions packages/celest_auth/example/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:flutter_lints/flutter.yaml
13 changes: 13 additions & 0 deletions packages/celest_auth/example/android/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit a20fd2e

Please sign in to comment.