Skip to content

Conversation

@vfiruz97
Copy link

Copy link
Collaborator

@marcelomendoncasoares marcelomendoncasoares left a comment

Choose a reason for hiding this comment

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

Thanks for the quick iteration with the docs, @vfiruz97! Docs looks good overall, following the standard of the other IDPs very well. Made a few suggestions to clarify some points and reorganize some details to improve the UX.

On the custom overrides, I think we'll need to convert it into a menu following the same style of the IDPs - after all, it is just like an IDP. The file is currently very long and with parts that can be removed to avoid redundancy.

Ping me back when you are ready for another review!

@@ -0,0 +1,252 @@
# Setup

To set up **Sign in with GitHub**, you must create OAuth2 credentials on [GitHub](https://github.com/settings/apps) and configure your Serverpod application accordingly. Since this provider is built on a generic OAuth2 utility, you can learn how to create a custom provider in the [Custom Providers](../10-custom-providers/02-oauth2-utility.md) section.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: No need to mention the custom utility here, since users of the GitHub provider do not need to know it. It is more relevant on the contrary: mention the GitHub IDP as an example on the custom provider docs.

Suggested change
To set up **Sign in with GitHub**, you must create OAuth2 credentials on [GitHub](https://github.com/settings/apps) and configure your Serverpod application accordingly. Since this provider is built on a generic OAuth2 utility, you can learn how to create a custom provider in the [Custom Providers](../10-custom-providers/02-oauth2-utility.md) section.
To set up **Sign in with GitHub**, you must create OAuth2 credentials on [GitHub](https://github.com/settings/apps) and configure your Serverpod application accordingly.

Comment on lines +13 to +19
- **GitHub Apps** (Recommended):
- Supports multiple redirect URIs (up to 10).
- Allows custom URI schemes (essential for mobile apps).
- More flexible and secure for modern applications.
- **OAuth Apps**:
- Only one redirect URI (must be HTTPS).
- No support for custom schemes (not ideal for mobile).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: It might be more interesting to describe the difference rom both by the conceptual difference rather than configurations only. For example:

Suggested change
- **GitHub Apps** (Recommended):
- Supports multiple redirect URIs (up to 10).
- Allows custom URI schemes (essential for mobile apps).
- More flexible and secure for modern applications.
- **OAuth Apps**:
- Only one redirect URI (must be HTTPS).
- No support for custom schemes (not ideal for mobile).
- **GitHub Apps**: more suitable when building an integration or bot that should belong to an organization or repo, have its own bot identity, keep working regardless of which human users come and go, and only see the repositories and permissions that are explicitly granted.
- **OAuth Apps**: preferred when the main need is to “Sign in with GitHub” or to perform actions purely as the currently logged‑in user using broad OAuth scopes. It is similar to any other third‑party OAuth provider, like Google or Apple, and can access the user’s GitHub resources within the broad scopes the user has granted.

- No support for custom schemes (not ideal for mobile).

:::tip
For most use cases, especially mobile, use [GitHub Apps](https://github.com/settings/apps).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: Add a link to the official docs that describe the purpose of each.

Comment on lines +27 to +40
1. Go to [GitHub Developer Settings](https://github.com/settings/apps).
2. Click **New GitHub App** (recommended) or **New OAuth App**.
3. Fill in the required fields:
- **App name**
- **Homepage URL**
- **Callback URL(s)** (use your app's redirect URI, e.g., `myapp://auth` for mobile)
- **Permissions** as needed
4. Save and generate the **Client ID** and **Client Secret**.

![GitHub App Setup](/img/authentication/providers/github/1-register-app.png)
![GitHub App Setup](/img/authentication/providers/github/2-add-permission.png)
![GitHub App Setup](/img/authentication/providers/github/3-add-permission.png)
Copy the **Client ID** and **Client Secret** for later use.
![GitHub App Setup](/img/authentication/providers/github/4-get-credentials.png)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: Move the images to after the steps they are related to so users can follow through guided by the images.

Comment on lines +29 to +34
3. Fill in the required fields:
- **App name**
- **Homepage URL**
- **Callback URL(s)** (use your app's redirect URI, e.g., `myapp://auth` for mobile)
- **Permissions** as needed
4. Save and generate the **Client ID** and **Client Secret**.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Question: Is the webhook URL needed? Since it comes enabled by default, we should suggest disabling, but also explain on a tip for what it serves.

image

Comment on lines +168 to +195
Create an HTML callback page in your `./web` folder (e.g., `auth.html`):

```html
<!DOCTYPE html>
<title>Authentication complete</title>
<p>Authentication is complete. If this does not happen automatically, please close the window.</p>
<script>
function postAuthenticationMessage() {
const message = {
'flutter-web-auth-2': window.location.href
};

if (window.opener) {
window.opener.postMessage(message, window.location.origin);
window.close();
} else if (window.parent && window.parent !== window) {
window.parent.postMessage(message, window.location.origin);
} else {
localStorage.setItem('flutter-web-auth-2', window.location.href);
window.close();
}
}

postAuthenticationMessage();
</script>
```

Configure your redirect URI to point to this file: `https://yourdomain.com/auth.html`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Question: If I have the GitHub IDP configured, can I reuse the same file? If so, we should add a note that one file is enough and shared between all IDPs that use the Oauth2 utility. We should also have this note on each IDP.

Comment on lines +201 to +255
Create a server-side configuration for token exchange:

```dart
import 'package:serverpod_auth_idp_server/core.dart';

final config = OAuth2PkceServerConfig(
// Token endpoint URL for exchanging authorization codes
tokenEndpointUrl: Uri.https('oauth.provider.com', '/oauth/token'),

// OAuth client ID (must match client-side)
clientId: pod.getPassword('myProviderClientId')!,

// OAuth client secret (keep secure!)
clientSecret: pod.getPassword('myProviderClientSecret')!,

// Function to extract access token from provider response
parseAccessToken: (data) {
// Handle provider errors
final error = data['error'] as String?;
if (error != null) {
throw OAuth2InvalidResponseException(
'Provider error: $error',
);
}

// Extract access token
final accessToken = data['access_token'] as String?;
if (accessToken == null) {
throw const OAuth2MissingAccessTokenException(
'No access token in response',
);
}

return accessToken;
},

// Optional: Where to send credentials (default: header)
credentialsLocation: OAuth2CredentialsLocation.header,

// Optional: Custom parameter names for credentials
clientIdKey: 'client_id',
clientSecretKey: 'client_secret',

// Optional: Custom headers for token requests
tokenRequestHeaders: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},

// Optional: Additional parameters for token exchange
tokenRequestParams: {
'grant_type': 'authorization_code',
},
);
```
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: We can simplify this snippet on the guide by replacing the parse function body by a generic comment like // Your parse logic here. Every detail that we can simplify in the first guide without sacrificing the complete configuration of a working usage is worthy.

Comment on lines +633 to +712
### Performance

1. **Reuse OAuth2PkceUtil Instances**: Create instances once and reuse them rather than recreating for each request.
2. **Handle Token Expiration**: Implement token refresh logic if your provider supports refresh tokens.
3. **Cache User Information**: Cache user profile data to reduce API calls to the provider.

## Reference

### Client-Side API

#### OAuth2PkceProviderClientConfig

Configuration for client-side OAuth2 flow.

**Constructor Parameters:**

- `authorizationEndpoint` (Uri): Provider's authorization endpoint
- `clientId` (String): OAuth client ID
- `redirectUri` (String): Callback URI
- `callbackUrlScheme` (String): URL scheme for callback
- `defaultScopes` (List\<String\>): Default permission scopes
- `additionalAuthParams` (Map\<String, String\>): Extra authorization parameters
- `scopeSeparator` (String): Scope joining separator (default: ' ')
- `enableState` (bool): Enable state parameter (default: true)
- `enablePKCE` (bool): Enable PKCE (default: true)

#### OAuth2PkceUtil (Client)

Manages client-side OAuth2 authorization flow.

**Constructor:**

```dart
OAuth2PkceUtil({
required OAuth2PkceProviderClientConfig config,
bool? useWebview,
})
```

**Methods:**

- `authorize({List<String>? scopes, Map<String, String>? authCodeParams})`: Initiates authorization flow

**Returns:** `OAuth2PkceResult` with `code` and `codeVerifier?`

### Server-Side API

#### OAuth2PkceServerConfig

Configuration for server-side token exchange.

**Constructor Parameters:**

- `tokenEndpointUrl` (Uri): Provider's token endpoint
- `clientId` (String): OAuth client ID
- `clientSecret` (String): OAuth client secret
- `parseAccessToken` (Function): Token parsing function
- `clientIdKey` (String): Client ID parameter name (default: 'client_id')
- `clientSecretKey` (String): Client secret parameter name (default: 'client_secret')
- `credentialsLocation` (OAuth2CredentialsLocation): Where to send credentials (default: header)
- `tokenRequestHeaders` (Map\<String, String\>): Request headers
- `tokenRequestParams` (Map\<String, dynamic\>): Extra request parameters

#### OAuth2PkceUtil (Server)

Manages server-side token exchange.

**Constructor:**

```dart
OAuth2PkceUtil({
required OAuth2PkceServerConfig config,
})
```

**Methods:**

- `exchangeCodeForToken({required String code, String? codeVerifier, required String redirectUri, http.Client? httpClient})`: Exchanges authorization code for access token

**Returns:** `String` (access token)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: This feels redundant with what was already presented.

Comment on lines +336 to +338
## Complete Example: Custom Provider

Here's a complete example implementing a custom OAuth2 provider:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: The complete implementation is interesting as a starting point for users, but it bloats the guide a lot. We should move this to a separate page instead and reference it here.

Comment on lines +340 to +385
### Step 1: Client Configuration

```dart
// lib/src/my_provider_config.dart
import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart';

class MyProviderConfig {
static OAuth2PkceProviderClientConfig get clientConfig {
return OAuth2PkceProviderClientConfig(
authorizationEndpoint: Uri.https(
'oauth.myprovider.com',
'/oauth/authorize',
),
clientId: const String.fromEnvironment('MY_PROVIDER_CLIENT_ID'),
redirectUri: 'myapp://auth-callback',
callbackUrlScheme: 'myapp',
defaultScopes: ['profile', 'email'],
);
}
}
```

### Step 2: Client Service

```dart
// lib/src/my_provider_service.dart
import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart';
import 'my_provider_config.dart';

class MyProviderService {
static final instance = MyProviderService._();
MyProviderService._();

late final OAuth2PkceUtil _oauth2Util;

void initialize() {
_oauth2Util = OAuth2PkceUtil(
config: MyProviderConfig.clientConfig,
);
}

Future<OAuth2PkceResult> signIn() async {
return await _oauth2Util.authorize();
}
}
```
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: Also follow the flow of server > client here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants