Skip to content
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

feat: Celest Auth #67

Merged
merged 1 commit into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 (iOS)
working-directory: packages/celest_auth/example
run: flutter build ios --no-codesign
- 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
Loading