Skip to main content

stakpak_shared/oauth/providers/
openrouter.rs

1//! OpenRouter API key auth provider
2
3use crate::models::auth::ProviderAuth;
4use crate::oauth::config::OAuthConfig;
5use crate::oauth::error::{OAuthError, OAuthResult};
6use crate::oauth::flow::TokenResponse;
7use crate::oauth::provider::{AuthMethod, OAuthProvider};
8use async_trait::async_trait;
9use reqwest::header::HeaderMap;
10
11/// OpenRouter provider.
12pub struct OpenRouterProvider;
13
14impl OpenRouterProvider {
15    /// Create a new OpenRouter provider.
16    pub fn new() -> Self {
17        Self
18    }
19}
20
21impl Default for OpenRouterProvider {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27#[async_trait]
28impl OAuthProvider for OpenRouterProvider {
29    fn id(&self) -> &'static str {
30        "openrouter"
31    }
32
33    fn name(&self) -> &'static str {
34        "OpenRouter"
35    }
36
37    fn auth_methods(&self) -> Vec<AuthMethod> {
38        vec![AuthMethod::api_key(
39            "api-key",
40            "API Key",
41            Some("Enter an existing OpenRouter API key".to_string()),
42        )]
43    }
44
45    fn oauth_config(&self, _method_id: &str) -> Option<OAuthConfig> {
46        None
47    }
48
49    async fn post_authorize(
50        &self,
51        method_id: &str,
52        _tokens: &TokenResponse,
53    ) -> OAuthResult<ProviderAuth> {
54        Err(OAuthError::unknown_method(method_id))
55    }
56
57    fn apply_auth_headers(&self, auth: &ProviderAuth, headers: &mut HeaderMap) -> OAuthResult<()> {
58        let api_key = match auth {
59            ProviderAuth::Api { key } => key,
60            ProviderAuth::OAuth { access, .. } => access,
61        };
62
63        headers.insert(
64            "authorization",
65            format!("Bearer {}", api_key)
66                .parse()
67                .map_err(|_| OAuthError::InvalidHeader)?,
68        );
69        Ok(())
70    }
71
72    fn api_key_env_var(&self) -> Option<&'static str> {
73        Some("OPENROUTER_API_KEY")
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_provider_id_name_and_methods() {
83        let provider = OpenRouterProvider::new();
84        let methods = provider.auth_methods();
85
86        assert_eq!(provider.id(), "openrouter");
87        assert_eq!(provider.name(), "OpenRouter");
88        assert_eq!(methods.len(), 1);
89        assert_eq!(methods[0].id, "api-key");
90    }
91
92    #[test]
93    fn test_oauth_config_is_not_supported() {
94        let provider = OpenRouterProvider::new();
95        assert!(provider.oauth_config("api-key").is_none());
96    }
97
98    #[test]
99    fn test_apply_auth_headers_api_key() {
100        let provider = OpenRouterProvider::new();
101        let auth = ProviderAuth::api_key("sk-or-v1-test-key");
102        let mut headers = HeaderMap::new();
103
104        let result = provider.apply_auth_headers(&auth, &mut headers);
105        assert!(result.is_ok());
106        assert_eq!(
107            headers.get("authorization"),
108            Some(&"Bearer sk-or-v1-test-key".parse().expect("valid header"))
109        );
110    }
111}