Skip to content

Commit a253318

Browse files
CopilotMathiasVDACopilot
authored
feat: added OAuth 2.0 authentication with PKCE and automatic token refresh (#66)
* Initial plan * Add OAuth 2.0 authentication types and core functionality Co-authored-by: MathiasVDA <15101339+MathiasVDA@users.noreply.github.com> * Add OAuth 2.0 styling, tests, and documentation Co-authored-by: MathiasVDA <15101339+MathiasVDA@users.noreply.github.com> * Add help link and OAuth2 provider examples (Azure, AWS, Keycloak) Co-authored-by: MathiasVDA <15101339+MathiasVDA@users.noreply.github.com> * Document OAuth2 redirect URI registration requirement Co-authored-by: MathiasVDA <15101339+MathiasVDA@users.noreply.github.com> * feat: added handling of id tokens * chore: reset this bit to main * Update packages/yasgui/src/Tab.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update docs/developer-guide.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/yasgui/src/TabSettingsModal.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix OAuth2 race conditions, memory leaks, and improve UX - Add unique flowId to prevent sessionStorage collisions across multiple OAuth flows - Add flowCompleted flag to prevent race condition between polling and messageHandler - Fix memory leaks by removing event listeners in all error paths - Improve alert messages with specific instructions on accessing Settings - Consolidate cleanup logic to prevent duplicate sessionStorage operations Co-authored-by: MathiasVDA <15101339+MathiasVDA@users.noreply.github.com> * Refactor: Extract closeModalAndRefresh helper to reduce duplication Extract repeated pattern of closing auth modal and refreshing endpoints list into a helper function. This reduces code duplication across all authentication types (basic, bearer, apiKey, oauth2) and improves maintainability. Co-authored-by: MathiasVDA <15101339+MathiasVDA@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: MathiasVDA <15101339+MathiasVDA@users.noreply.github.com> Co-authored-by: Mathias Vanden Auweele <mathias@matdata.eu> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 4517d24 commit a253318

File tree

12 files changed

+1171
-24
lines changed

12 files changed

+1171
-24
lines changed

docs/developer-guide.md

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,7 @@ interface RequestConfig {
10141014
basicAuth?: BasicAuthConfig | ((yasqe: Yasqe) => BasicAuthConfig | undefined);
10151015
bearerAuth?: BearerAuthConfig | ((yasqe: Yasqe) => BearerAuthConfig | undefined);
10161016
apiKeyAuth?: ApiKeyAuthConfig | ((yasqe: Yasqe) => ApiKeyAuthConfig | undefined);
1017+
oauth2Auth?: OAuth2AuthConfig | ((yasqe: Yasqe) => OAuth2AuthConfig | undefined);
10171018
}
10181019

10191020
interface BasicAuthConfig {
@@ -1029,6 +1030,11 @@ interface ApiKeyAuthConfig {
10291030
headerName: string;
10301031
apiKey: string;
10311032
}
1033+
1034+
interface OAuth2AuthConfig {
1035+
accessToken: string;
1036+
idToken?: string;
1037+
}
10321038
```
10331039

10341040
#### Request Configuration Example
@@ -1067,11 +1073,12 @@ YASGUI supports multiple authentication methods for SPARQL endpoints: Basic Auth
10671073

10681074
#### Authentication Types
10691075

1070-
YASGUI supports three authentication types:
1076+
YASGUI supports four authentication types:
10711077

10721078
1. **Basic Authentication**: Username and password sent as HTTP Basic Auth
10731079
2. **Bearer Token**: Token sent in the `Authorization: Bearer <token>` header
10741080
3. **API Key**: Custom header with an API key (e.g., `X-API-Key: <key>`)
1081+
4. **OAuth 2.0**: Industry-standard OAuth 2.0 with automatic token refresh
10751082

10761083
#### Basic Authentication
10771084

@@ -1159,6 +1166,24 @@ yasgui.persistentConfig.addOrUpdateEndpoint("https://api.example.com/sparql", {
11591166
}
11601167
});
11611168

1169+
// Add or update an endpoint with OAuth 2.0
1170+
yasgui.persistentConfig.addOrUpdateEndpoint("https://oauth.example.com/sparql", {
1171+
label: "OAuth Protected Endpoint",
1172+
showAsButton: true,
1173+
authentication: {
1174+
type: 'oauth2',
1175+
clientId: 'your-client-id',
1176+
authorizationEndpoint: 'https://auth.example.com/oauth/authorize',
1177+
tokenEndpoint: 'https://auth.example.com/oauth/token',
1178+
redirectUri: 'https://yourapp.com/oauth2-callback', // optional
1179+
scope: 'read write', // optional
1180+
// The following are automatically populated after authentication:
1181+
// accessToken: '...',
1182+
// refreshToken: '...',
1183+
// tokenExpiry: 1234567890
1184+
}
1185+
});
1186+
11621187
// Get endpoint configuration
11631188
const config = yasgui.persistentConfig.getEndpointConfig("https://example.com/sparql");
11641189

@@ -1171,6 +1196,72 @@ yasgui.persistentConfig.addOrUpdateEndpoint("https://example.com/sparql", {
11711196
yasgui.persistentConfig.deleteEndpointConfig("https://example.com/sparql");
11721197
```
11731198

1199+
#### OAuth 2.0 Provider Examples
1200+
1201+
**⚠️ Important Prerequisite:**
1202+
Before OAuth 2.0 authentication can work, the **OAuth administrator must register the redirect URI** (callback URL) in the OAuth provider's application configuration. YASGUI uses the current page URL as the redirect URI by default (e.g., `https://yasgui.example.com/`). This URL must be added to the list of allowed redirect URIs in your OAuth application settings.
1203+
1204+
**Microsoft Azure (Entra ID)**
1205+
1206+
```javascript
1207+
yasgui.persistentConfig.addOrUpdateEndpoint("https://your-sparql-endpoint.com/sparql", {
1208+
label: "Azure Protected Endpoint",
1209+
authentication: {
1210+
type: 'oauth2',
1211+
clientId: 'your-azure-client-id',
1212+
authorizationEndpoint: 'https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize',
1213+
tokenEndpoint: 'https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token',
1214+
scope: 'api://your-app-id/.default', // or specific scopes like 'openid profile'
1215+
redirectUri: window.location.origin + window.location.pathname // optional
1216+
}
1217+
});
1218+
```
1219+
1220+
**Redirect URI Registration:** In Azure AD app registration, add your YASGUI URL to "Redirect URIs" under Authentication settings.
1221+
1222+
**AWS Cognito**
1223+
1224+
```javascript
1225+
yasgui.persistentConfig.addOrUpdateEndpoint("https://your-sparql-endpoint.com/sparql", {
1226+
label: "AWS Cognito Protected Endpoint",
1227+
authentication: {
1228+
type: 'oauth2',
1229+
clientId: 'your-cognito-app-client-id',
1230+
authorizationEndpoint: 'https://your-domain.auth.region.amazoncognito.com/oauth2/authorize',
1231+
tokenEndpoint: 'https://your-domain.auth.region.amazoncognito.com/oauth2/token',
1232+
scope: 'openid profile', // adjust based on your needs
1233+
redirectUri: window.location.origin + window.location.pathname // optional
1234+
}
1235+
});
1236+
```
1237+
1238+
**Redirect URI Registration:** In Cognito app client settings, add your YASGUI URL to "Allowed callback URLs".
1239+
1240+
**Keycloak**
1241+
1242+
```javascript
1243+
yasgui.persistentConfig.addOrUpdateEndpoint("https://your-sparql-endpoint.com/sparql", {
1244+
label: "Keycloak Protected Endpoint",
1245+
authentication: {
1246+
type: 'oauth2',
1247+
clientId: 'your-keycloak-client-id',
1248+
authorizationEndpoint: 'https://your-keycloak-domain.com/realms/{realm-name}/protocol/openid-connect/auth',
1249+
tokenEndpoint: 'https://your-keycloak-domain.com/realms/{realm-name}/protocol/openid-connect/token',
1250+
scope: 'openid profile', // adjust based on your client configuration
1251+
redirectUri: window.location.origin + window.location.pathname // optional
1252+
}
1253+
});
1254+
```
1255+
1256+
**Redirect URI Registration:** In Keycloak client configuration, add your YASGUI URL to "Valid Redirect URIs".
1257+
1258+
**Important Notes:**
1259+
- Replace placeholders like `{tenant-id}`, `{realm-name}`, `region`, etc. with your actual values
1260+
- **The OAuth administrator must register the redirect URI** in the OAuth provider before authentication will work
1261+
- For Azure, the client must be registered in Azure AD with public client flow enabled
1262+
- For AWS Cognito, the app client should have "Authorization code grant" flow enabled
1263+
- For Keycloak, the client should have "Standard Flow" enabled and "Access Type" set to "public"
1264+
11741265
#### Dynamic Authentication
11751266

11761267
Use a function to dynamically provide credentials:

docs/user-guide.md

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -709,23 +709,53 @@ The endpoints table shows:
709709

710710
**Configuring Authentication:**
711711

712-
YASGUI supports HTTP Basic Authentication for endpoints that require username and password credentials.
712+
YASGUI supports multiple authentication methods for endpoints that require credentials.
713713

714714
1. **Find your endpoint** in the SPARQL Endpoints table
715715
2. **Click "Configure"** in the Authentication column
716-
3. **Select authentication type** (currently HTTP Basic Authentication)
717-
4. **Enter credentials**:
718-
- Username: Your endpoint username
719-
- Password: Your endpoint password
720-
5. **Click "Save"** to apply
716+
3. **Select authentication type**:
717+
- **HTTP Basic Authentication**: Username and password
718+
- **Bearer Token**: Pre-configured access token
719+
- **API Key**: Custom header with API key
720+
- **OAuth 2.0**: Industry-standard OAuth 2.0 authorization
721+
4. **Enter credentials** based on your selected type (see below)
722+
5. **Click "Save"** (or "Save & Authenticate" for OAuth 2.0) to apply
723+
724+
**Authentication Types:**
725+
726+
*HTTP Basic Authentication:*
727+
- Username: Your endpoint username
728+
- Password: Your endpoint password
729+
- Use case: Simple username/password authentication
730+
731+
*Bearer Token:*
732+
- Token: Your pre-configured bearer token
733+
- Use case: When you have a pre-generated access token
734+
735+
*API Key (Custom Header):*
736+
- Header Name: The HTTP header name (e.g., X-API-Key)
737+
- API Key: Your API key value
738+
- Use case: Endpoints using custom header-based authentication
739+
740+
*OAuth 2.0:*
741+
- Client ID: Your OAuth application's client ID
742+
- Authorization Endpoint: The OAuth provider's authorization URL
743+
- Token Endpoint: The OAuth provider's token exchange URL
744+
- Redirect URI: Callback URL (optional, defaults to current page)
745+
- Scope: Space-separated OAuth scopes (optional)
746+
- Use case: Secure, industry-standard authorization with automatic token refresh
747+
- **Process**: Click "Save & Authenticate" to open OAuth login window
748+
- **⚠️ Important**: The redirect URI must be registered with your OAuth provider by the OAuth administrator before authentication will work
721749

722750
**Security Considerations:**
723751

724752
⚠️ **Important Security Notes:**
725753

726-
- **Credentials are stored in browser localStorage**: Your username and password are stored locally in your browser
754+
- **Credentials are stored in browser localStorage**: Your authentication credentials are stored locally in your browser
727755
- **Only use with HTTPS endpoints**: Never send credentials to HTTP endpoints as they will be transmitted in plain text
728756
- **Be cautious on shared computers**: Clear your browser data when using YASGUI on shared or public computers
757+
- **OAuth 2.0 tokens**: Access tokens are automatically refreshed when expired (if refresh token is available)
758+
- **Token security**: OAuth 2.0 uses secure PKCE flow (Proof Key for Code Exchange) for enhanced security
729759

730760
**How Authentication Works:**
731761

@@ -735,10 +765,57 @@ Authentication is stored per-endpoint, which means:
735765
- Credentials persist across browser sessions (stored in localStorage)
736766

737767
When authentication is configured:
768+
769+
For **Basic Authentication**:
738770
1. YASGUI encodes your credentials using Base64 encoding
739771
2. Adds an `Authorization` header with the format: `Basic <encoded-credentials>`
740772
3. Sends this header with every SPARQL query request to that endpoint
741773

774+
For **Bearer Token**:
775+
1. Uses the provided token as-is
776+
2. Adds an `Authorization` header with the format: `Bearer <token>`
777+
3. Sends this header with every SPARQL query request to that endpoint
778+
779+
For **API Key**:
780+
1. Uses the specified custom header name and API key value
781+
2. Adds a custom header with the format: `<Header-Name>: <api-key>`
782+
3. Sends this header with every SPARQL query request to that endpoint
783+
784+
For **OAuth 2.0**:
785+
1. Opens a popup window for OAuth provider authentication
786+
2. Uses Authorization Code flow with PKCE for secure token exchange
787+
3. Stores access token and refresh token
788+
4. Automatically checks token expiration before each query
789+
5. Automatically refreshes expired tokens using refresh token (if available)
790+
6. Adds an `Authorization` header with the format: `Bearer <access-token>`
791+
7. If token refresh fails, prompts user to re-authenticate
792+
793+
**OAuth 2.0 Provider Examples:**
794+
795+
**⚠️ Important Prerequisite:**
796+
Before using OAuth 2.0, the OAuth administrator must register the redirect URI (callback URL) in the OAuth provider's configuration. By default, YASGUI uses the current page URL as the redirect URI. For example, if YASGUI is hosted at `https://yasgui.example.com/`, this URL must be added to the allowed redirect URIs in your OAuth application settings.
797+
798+
*Microsoft Azure (Entra ID):*
799+
- Authorization Endpoint: `https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize`
800+
- Token Endpoint: `https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token`
801+
- Scope: `api://your-app-id/.default` or `openid profile`
802+
- Note: Your app must be registered in Azure AD with public client flow enabled
803+
- **Redirect URI Registration**: Add your YASGUI URL to "Redirect URIs" in Azure AD app registration
804+
805+
*AWS Cognito:*
806+
- Authorization Endpoint: `https://your-domain.auth.region.amazoncognito.com/oauth2/authorize`
807+
- Token Endpoint: `https://your-domain.auth.region.amazoncognito.com/oauth2/token`
808+
- Scope: `openid profile` (adjust as needed)
809+
- Note: Enable "Authorization code grant" flow in your app client settings
810+
- **Redirect URI Registration**: Add your YASGUI URL to "Allowed callback URLs" in Cognito app client settings
811+
812+
*Keycloak:*
813+
- Authorization Endpoint: `https://your-keycloak-domain.com/realms/{realm-name}/protocol/openid-connect/auth`
814+
- Token Endpoint: `https://your-keycloak-domain.com/realms/{realm-name}/protocol/openid-connect/token`
815+
- Scope: `openid profile` (adjust based on client configuration)
816+
- Note: Client should have "Standard Flow" enabled and "Access Type" set to "public"
817+
- **Redirect URI Registration**: Add your YASGUI URL to "Valid Redirect URIs" in Keycloak client configuration
818+
742819
### Query History and Persistence
743820

744821
YASGUI automatically saves your work locally.

packages/yasgui/src/ConfigExportImport.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ export function parseFromTurtle(turtle: string): Partial<PersistedJson> {
307307
basicAuth: undefined,
308308
bearerAuth: undefined,
309309
apiKeyAuth: undefined,
310+
oauth2Auth: undefined,
310311
},
311312
yasr: {
312313
settings: {},

0 commit comments

Comments
 (0)