Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
104 changes: 104 additions & 0 deletions docs/libraries/api-clients/go.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,110 @@ response, err := client.Client.Chat.Create(ctx, &glean.ChatRequest{
})
```

### OAuth Authentication

OAuth allows you to use access tokens from your identity provider (Google, Azure, Okta, etc.) instead of Glean-issued tokens.

:::info Prerequisites
- OAuth enabled in [Glean Admin > Third-Party OAuth](https://app.glean.com/admin/setup/third-party-oauth)
- Your OAuth Client ID registered with Glean
- See [OAuth Setup Guide](https://docs.glean.com/administration/oauth/oauth-idp) for admin configuration
:::

OAuth requests require these headers:

| Header | Value |
|--------|-------|
| `Authorization` | `Bearer <oauth_access_token>` |
| `X-Glean-Auth-Type` | `OAUTH` |

#### Example: Authorization Code Flow

This example uses [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2):

```go
package main

import (
"context"
"encoding/json"
"net/http"
"os"

"golang.org/x/oauth2"
glean "github.com/gleanwork/api-client-go"
)

var oauthConfig = &oauth2.Config{
ClientID: os.Getenv("OAUTH_CLIENT_ID"),
ClientSecret: os.Getenv("OAUTH_CLIENT_SECRET"),
RedirectURL: "http://localhost:8080/callback",
Scopes: []string{"openid", "email"},

Choose a reason for hiding this comment

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

3/5 (strong opinion: non-blocking)

This should include offline_access to showcase getting a refresh token

Endpoint: oauth2.Endpoint{
AuthURL: os.Getenv("OAUTH_AUTH_URL"),
TokenURL: os.Getenv("OAUTH_TOKEN_URL"),
},
}

// oauthTransport adds OAuth headers to all requests
type oauthTransport struct {
token string
transport http.RoundTripper
}

func (t *oauthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("Authorization", "Bearer "+t.token)
req.Header.Set("X-Glean-Auth-Type", "OAUTH")
return t.transport.RoundTrip(req)
}

func main() {
http.HandleFunc("/login", handleLogin)
http.HandleFunc("/callback", handleCallback)
http.ListenAndServe(":8080", nil)
}

func handleLogin(w http.ResponseWriter, r *http.Request) {
url := oauthConfig.AuthCodeURL("state")
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
Comment on lines 349 to 367

Choose a reason for hiding this comment

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

4/5 (serious issue: blocking)

Have we validated this code works? We're OAuth 2.1 so state is optional and not even recommended but PKCE is required.

That we're generating a state suggests we might be not generating a PKCE challenge.


func handleCallback(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code")
token, err := oauthConfig.Exchange(context.Background(), code)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// Create HTTP client with OAuth headers
httpClient := &http.Client{
Transport: &oauthTransport{
token: token.AccessToken,
transport: http.DefaultTransport,
},
}

// Create Glean client with custom HTTP client
client := glean.New(
glean.WithInstance(os.Getenv("GLEAN_INSTANCE")),
glean.WithClient(httpClient),
)

results, err := client.Client.Search.Query(r.Context(), "quarterly reports", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

json.NewEncoder(w).Encode(results)
}
```

:::tip
Access tokens typically expire after ~1 hour. For production use, use `oauth2.Config.TokenSource` for automatic refresh.
:::

## Error Handling

```go
Expand Down
104 changes: 104 additions & 0 deletions docs/libraries/api-clients/java.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,110 @@ glean:
instance: ${GLEAN_INSTANCE}
```

### OAuth Authentication

OAuth allows you to use access tokens from your identity provider (Google, Azure, Okta, etc.) instead of Glean-issued tokens.

:::info Prerequisites
- OAuth enabled in [Glean Admin > Third-Party OAuth](https://app.glean.com/admin/setup/third-party-oauth)
- Your OAuth Client ID registered with Glean
- See [OAuth Setup Guide](https://docs.glean.com/administration/oauth/oauth-idp) for admin configuration
:::

OAuth requests require these headers:

| Header | Value |
|--------|-------|
| `Authorization` | `Bearer <oauth_access_token>` |
| `X-Glean-Auth-Type` | `OAUTH` |

#### Example: Authorization Code Flow with Spring Security

This example uses Spring Security OAuth2 Client:

```yaml
# application.yml
spring:
security:
oauth2:
client:
registration:
provider:
client-id: ${OAUTH_CLIENT_ID}
client-secret: ${OAUTH_CLIENT_SECRET}
scope: openid,email
provider:
provider:
issuer-uri: ${OAUTH_ISSUER}

glean:
instance: ${GLEAN_INSTANCE}
```

```java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
import org.springframework.web.bind.annotation.*;
import com.glean.api_client.glean_api_client.Glean;
import com.glean.api_client.glean_api_client.models.components.*;
import com.glean.api_client.glean_api_client.utils.HTTPClient;
import com.glean.api_client.glean_api_client.utils.Utils;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;

@RestController
public class GleanOAuthController {

@Value("${glean.instance}")
private String gleanInstance;

@GetMapping("/search")
public Map<String, Object> search(
@RegisteredOAuth2AuthorizedClient("provider") OAuth2AuthorizedClient oauthClient,
@RequestParam String query) throws Exception {

String accessToken = oauthClient.getAccessToken().getTokenValue();

// Create HTTP client that adds OAuth headers
HTTPClient httpClient = new HTTPClient() {
private final HTTPClient defaultClient = new HTTPClient();

@Override
public HttpResponse<java.io.InputStream> send(HttpRequest request)
throws java.io.IOException, InterruptedException, java.net.URISyntaxException {
HttpRequest modifiedRequest = Utils.copy(request)
.header("Authorization", "Bearer " + accessToken)
.header("X-Glean-Auth-Type", "OAUTH")
.build();
return defaultClient.send(modifiedRequest);
}
};

// Create Glean client with custom HTTP client
Glean glean = Glean.builder()
.instance(gleanInstance)
.client(httpClient)
.build();

var response = glean.client().search().query()
.query(query)
.pageSize(10)
.call();

return Map.of("results", response.searchResponse()
.map(SearchResponse::results)
.orElse(List.of()));
}
}
```

:::tip
Access tokens typically expire after ~1 hour. Spring Security OAuth2 Client handles token refresh automatically when configured with a refresh token.
:::

## Error Handling

```java
Expand Down
75 changes: 75 additions & 0 deletions docs/libraries/api-clients/python.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,81 @@ response = client.client.chat.create(
)
```

### OAuth Authentication

OAuth allows you to use access tokens from your identity provider (Google, Azure, Okta, etc.) instead of Glean-issued tokens.

:::info Prerequisites
- OAuth enabled in [Glean Admin > Third-Party OAuth](https://app.glean.com/admin/setup/third-party-oauth)
- Your OAuth Client ID registered with Glean
- See [OAuth Setup Guide](https://docs.glean.com/administration/oauth/oauth-idp) for admin configuration
:::

OAuth requests require these headers:

| Header | Value |
|--------|-------|
| `Authorization` | `Bearer <oauth_access_token>` |
| `X-Glean-Auth-Type` | `OAUTH` |

#### Example: Authorization Code Flow

This example uses [Authlib](https://pypi.org/project/Authlib/) with Flask:

```python
import os
import httpx
from flask import Flask, redirect, request, jsonify
from authlib.integrations.flask_client import OAuth
from glean.api_client import Glean

app = Flask(__name__)
app.secret_key = os.urandom(24)

oauth = OAuth(app)
oauth.register(
name='provider',
client_id=os.getenv('OAUTH_CLIENT_ID'),
client_secret=os.getenv('OAUTH_CLIENT_SECRET'),
server_metadata_url=os.getenv('OAUTH_ISSUER') + '/.well-known/openid-configuration',
client_kwargs={'scope': 'openid email'},
)

@app.route('/login')
def login():
redirect_uri = 'http://localhost:5000/callback'
return oauth.provider.authorize_redirect(redirect_uri)

@app.route('/callback')
def callback():
token = oauth.provider.authorize_access_token()

# Create HTTP client with OAuth headers
http_client = httpx.Client(headers={
'Authorization': f"Bearer {token['access_token']}",
'X-Glean-Auth-Type': 'OAUTH',
})

# Use OAuth token with Glean
with Glean(
instance=os.getenv('GLEAN_INSTANCE'),
client=http_client,
) as glean:
results = glean.client.search.query(
query='quarterly reports',
page_size=10,
)

return jsonify(results.to_dict())

if __name__ == '__main__':
app.run(port=5000)
```

:::tip
Access tokens typically expire after ~1 hour. For production use, implement token refresh using `token['refresh_token']`.
:::

## Error Handling

```python
Expand Down
82 changes: 82 additions & 0 deletions docs/libraries/api-clients/typescript.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,88 @@ const response = await client.client.chat.create({
});
```

### OAuth Authentication

OAuth allows you to use access tokens from your identity provider (Google, Azure, Okta, etc.) instead of Glean-issued tokens.

:::info Prerequisites
- OAuth enabled in [Glean Admin > Third-Party OAuth](https://app.glean.com/admin/setup/third-party-oauth)
- Your OAuth Client ID registered with Glean
- See [OAuth Setup Guide](https://docs.glean.com/administration/oauth/oauth-idp) for admin configuration
:::

OAuth requests require these headers:

| Header | Value |
|--------|-------|
| `Authorization` | `Bearer <oauth_access_token>` |
| `X-Glean-Auth-Type` | `OAUTH` |

#### Example: Authorization Code Flow

This example uses [openid-client](https://www.npmjs.com/package/openid-client) with Express:

```typescript
import express from 'express';
import * as client from 'openid-client';
import { Glean } from '@gleanwork/api-client';

const config = {
clientId: process.env.OAUTH_CLIENT_ID!,
clientSecret: process.env.OAUTH_CLIENT_SECRET!,
redirectUri: 'http://localhost:3000/callback',
issuer: process.env.OAUTH_ISSUER!, // e.g., https://accounts.google.com
};

const app = express();
let oidcConfig: client.Configuration;

// Initialize OIDC client
async function init() {
oidcConfig = await client.discovery(
new URL(config.issuer),
config.clientId,
config.clientSecret
);
}

app.get('/login', (req, res) => {
const authUrl = client.buildAuthorizationUrl(oidcConfig, {
redirect_uri: config.redirectUri,
scope: 'openid email',
});
res.redirect(authUrl.href);
});

app.get('/callback', async (req, res) => {
const callbackUrl = new URL(req.url, `http://${req.headers.host}`);
const tokens = await client.authorizationCodeGrant(oidcConfig, callbackUrl, {
expectedState: client.skipStateCheck,
});

// Use OAuth token with Glean
const glean = new Glean({ instance: process.env.GLEAN_INSTANCE! });

const results = await glean.client.search.search({
query: 'quarterly reports',
pageSize: 10,
}, {
headers: {
'Authorization': `Bearer ${tokens.access_token}`,
'X-Glean-Auth-Type': 'OAUTH',
},
});

res.json(results);
});

init().then(() => app.listen(3000));
```

:::tip
Access tokens typically expire after ~1 hour. For production use, implement token refresh using `tokens.refresh_token`.
:::

## Error Handling

```typescript
Expand Down