0
# Authentication System
1
2
Safety CLI provides a comprehensive authentication system for accessing the Safety platform services. The system supports multiple authentication methods including OAuth2 token-based authentication and API key authentication.
3
4
## Core Authentication Architecture
5
6
### Authentication Models { .api }
7
8
**Import Statements:**
9
10
```python
11
from safety.auth.models import Auth, Organization, XAPIKeyAuth
12
from safety.auth.utils import SafetyAuthSession
13
from safety.auth.cli_utils import (
14
auth_options, proxy_options,
15
build_client_session, inject_session
16
)
17
from safety_schemas.models import Stage
18
```
19
20
#### Organization Model { .api }
21
22
**Description**: Represents a Safety platform organization for multi-tenant authentication.
23
24
```python
25
@dataclass
26
class Organization:
27
id: str # Unique organization identifier
28
name: str # Organization display name
29
30
def to_dict(self) -> Dict[str, Any]:
31
"""
32
Convert organization to dictionary format.
33
34
Returns:
35
Dict[str, Any]: Organization data as dictionary
36
"""
37
```
38
39
**Example Usage:**
40
41
```python
42
from safety.auth.models import Organization
43
44
# Create organization instance
45
org = Organization(id="12345", name="My Company")
46
org_dict = org.to_dict()
47
# Output: {"id": "12345", "name": "My Company"}
48
```
49
50
#### Auth Model { .api }
51
52
**Description**: Central authentication state container managing credentials, client sessions, and user information.
53
54
```python
55
@dataclass
56
class Auth:
57
org: Optional[Organization] # Associated organization
58
keys: Any # OAuth keys and certificates
59
client: Any # HTTP client session
60
code_verifier: str # OAuth2 PKCE code verifier
61
client_id: str # OAuth2 client identifier
62
stage: Optional[Stage] = Stage.development # Environment stage
63
email: Optional[str] = None # User email address
64
name: Optional[str] = None # User display name
65
email_verified: bool = False # Email verification status
66
```
67
68
**Methods:**
69
70
```python
71
def is_valid(self) -> bool:
72
"""
73
Validate current authentication state.
74
75
Returns:
76
bool: True if authentication is valid and active
77
"""
78
79
def refresh_from(self, info: Dict[str, Any]) -> None:
80
"""
81
Update authentication state from user information.
82
83
Args:
84
info (Dict[str, Any]): User profile information from OAuth provider
85
"""
86
87
def get_auth_method(self) -> str:
88
"""
89
Get current authentication method being used.
90
91
Returns:
92
str: Authentication method ("API Key", "Token", or "None")
93
"""
94
```
95
96
**Example Usage:**
97
98
```python
99
from safety.auth.models import Auth, Organization
100
from safety_schemas.models import Stage
101
102
# Create auth instance
103
auth = Auth(
104
org=Organization(id="123", name="My Org"),
105
keys={},
106
client=None,
107
code_verifier="abc123",
108
client_id="safety-cli",
109
stage=Stage.PRODUCTION,
110
email="user@company.com",
111
name="John Doe",
112
email_verified=True
113
)
114
115
# Check authentication validity
116
if auth.is_valid():
117
print(f"Authenticated as {auth.name} using {auth.get_auth_method()}")
118
```
119
120
### Authentication Session Management { .api }
121
122
#### SafetyAuthSession Class { .api }
123
124
**Description**: Extended OAuth2Session with Safety-specific authentication handling and API integration.
125
126
```python
127
class SafetyAuthSession(OAuth2Session):
128
def __init__(self, *args: Any, **kwargs: Any) -> None:
129
"""
130
Initialize Safety authentication session.
131
132
Args:
133
*args: Positional arguments for OAuth2Session
134
**kwargs: Keyword arguments for OAuth2Session
135
"""
136
137
# Authentication state properties
138
proxy_required: bool # Require proxy for requests
139
proxy_timeout: Optional[int] # Proxy timeout in seconds
140
api_key: Optional[str] # API key for authentication
141
```
142
143
**Authentication Methods:**
144
145
```python
146
def get_credential(self) -> Optional[str]:
147
"""
148
Get current authentication credential (API key or token).
149
150
Returns:
151
Optional[str]: Current credential or None if not authenticated
152
"""
153
154
def is_using_auth_credentials(self) -> bool:
155
"""
156
Check if session is configured with authentication credentials.
157
158
Note: This checks configuration, not validity of credentials.
159
160
Returns:
161
bool: True if credentials are configured
162
"""
163
164
def get_authentication_type(self) -> AuthenticationType:
165
"""
166
Determine the type of authentication being used.
167
168
Returns:
169
AuthenticationType: Current authentication type (api_key, token, or none)
170
"""
171
```
172
173
**HTTP Request Handling:**
174
175
```python
176
def request(
177
self,
178
method: str, # HTTP method (GET, POST, etc.)
179
url: str, # Request URL
180
withhold_token: bool = False, # Skip token authentication
181
auth: Optional[Tuple] = None, # Custom authentication
182
bearer: bool = True, # Use bearer token format
183
**kwargs: Any # Additional request parameters
184
) -> requests.Response:
185
"""
186
Make authenticated HTTP request with automatic credential handling.
187
188
Automatically adds appropriate authentication headers:
189
- X-Api-Key header for API key authentication
190
- Authorization Bearer header for token authentication
191
- Safety CLI metadata headers
192
193
Args:
194
method: HTTP method to use
195
url: Target URL for request
196
withhold_token: Skip automatic token authentication
197
auth: Custom authentication tuple
198
bearer: Use bearer token format
199
**kwargs: Additional parameters (headers, timeout, etc.)
200
201
Returns:
202
requests.Response: HTTP response object
203
204
Raises:
205
RequestTimeoutError: If request times out
206
NetworkConnectionError: If network connection fails
207
ServerError: If server returns error status
208
"""
209
```
210
211
### Authentication Types { .api }
212
213
```python
214
from safety.scan.util import AuthenticationType
215
216
class AuthenticationType(Enum):
217
NONE = "none" # No authentication
218
API_KEY = "api_key" # API key authentication
219
TOKEN = "token" # OAuth2 token authentication
220
```
221
222
### API Key Authentication { .api }
223
224
#### XAPIKeyAuth Class { .api }
225
226
**Description**: API key authentication handler for requests.
227
228
```python
229
class XAPIKeyAuth(BaseOAuth):
230
def __init__(self, api_key: str) -> None:
231
"""
232
Initialize API key authentication.
233
234
Args:
235
api_key (str): Safety platform API key
236
"""
237
238
def __call__(self, request: Any) -> Any:
239
"""
240
Add API key to request headers.
241
242
Args:
243
request: HTTP request object to modify
244
245
Returns:
246
Any: Modified request with X-API-Key header
247
"""
248
```
249
250
**Example Usage:**
251
252
```python
253
from safety.auth.models import XAPIKeyAuth
254
import requests
255
256
# Create API key authenticator
257
auth = XAPIKeyAuth("sk-12345abcdef")
258
259
# Use with requests
260
response = requests.get(
261
"https://api.safetycli.com/scan",
262
auth=auth
263
)
264
```
265
266
## Authentication Commands Integration
267
268
### CLI Authentication Decorators { .api }
269
270
**Description**: Decorators for integrating authentication into CLI commands.
271
272
```python
273
from safety.auth.cli_utils import auth_options, proxy_options, inject_session
274
275
@auth_options
276
@proxy_options
277
def my_command(ctx, ...):
278
"""Command with authentication support."""
279
# Authentication automatically injected into ctx.obj.auth
280
session = ctx.obj.auth.client
281
```
282
283
#### auth_options Decorator { .api }
284
285
**Description**: Adds authentication-related command line options.
286
287
**Added Options:**
288
289
```bash
290
--key TEXT # API key for authentication
291
--auth-org-id TEXT # Organization ID override
292
--auth-stage {development,staging,production} # Environment stage
293
```
294
295
#### proxy_options Decorator { .api }
296
297
**Description**: Adds proxy configuration options for corporate environments.
298
299
**Added Options:**
300
301
```bash
302
--proxy-protocol {http,https} # Proxy protocol
303
--proxy-host TEXT # Proxy hostname
304
--proxy-port INTEGER # Proxy port number
305
--proxy-timeout INTEGER # Proxy timeout in seconds
306
```
307
308
#### inject_session Decorator { .api }
309
310
**Description**: Injects authenticated session into command context.
311
312
**Usage Pattern:**
313
314
```python
315
@inject_session
316
def authenticated_command(ctx):
317
"""Command with automatic session injection."""
318
# Access authenticated session
319
session: SafetyAuthSession = ctx.obj.auth.client
320
321
# Make authenticated requests
322
response = session.get("https://api.safetycli.com/user/profile")
323
```
324
325
### Session Building { .api }
326
327
#### build_client_session Function { .api }
328
329
**Description**: Factory function for creating authenticated client sessions.
330
331
```python
332
def build_client_session(
333
api_key: Optional[str] = None, # API key override
334
proxy_protocol: Optional[str] = None, # Proxy protocol
335
proxy_host: Optional[str] = None, # Proxy hostname
336
proxy_port: Optional[int] = None, # Proxy port
337
proxy_timeout: Optional[int] = None, # Proxy timeout
338
organization: Optional[Organization] = None, # Target organization
339
stage: Optional[Stage] = None # Environment stage
340
) -> SafetyAuthSession:
341
"""
342
Build authenticated client session with specified configuration.
343
344
Args:
345
api_key: API key for authentication (overrides stored credentials)
346
proxy_protocol: HTTP/HTTPS proxy protocol
347
proxy_host: Proxy server hostname
348
proxy_port: Proxy server port number
349
proxy_timeout: Timeout for proxy connections
350
organization: Target organization for multi-tenant access
351
stage: Environment stage for configuration
352
353
Returns:
354
SafetyAuthSession: Configured authenticated session
355
356
Raises:
357
InvalidCredentialError: If provided credentials are invalid
358
NetworkConnectionError: If proxy configuration is invalid
359
"""
360
```
361
362
**Example Usage:**
363
364
```python
365
from safety.auth.cli_utils import build_client_session
366
from safety.auth.models import Organization
367
from safety_schemas.models import Stage
368
369
# Build session with API key
370
session = build_client_session(
371
api_key="sk-12345abcdef",
372
organization=Organization(id="123", name="My Org"),
373
stage=Stage.PRODUCTION
374
)
375
376
# Build session with proxy
377
session = build_client_session(
378
proxy_protocol="https",
379
proxy_host="proxy.company.com",
380
proxy_port=8080,
381
proxy_timeout=30
382
)
383
```
384
385
## Platform API Integration
386
387
### Core API Endpoints { .api }
388
389
The SafetyAuthSession provides methods for interacting with Safety platform APIs:
390
391
```python
392
# User and organization management
393
session.get_user_info() -> Dict[str, Any] # Get user profile
394
session.get_organizations() -> List[Organization] # List organizations
395
396
# Project and scan management
397
session.create_project(name: str, org_id: str) -> Dict # Create project
398
session.upload_requirements(payload: Dict) -> Response # Upload scan data
399
session.download_policy(
400
project_id: str,
401
stage: Stage,
402
branch: Optional[str]
403
) -> Optional[Dict] # Download policy
404
405
# Vulnerability data access
406
session.get_vulnerabilities(packages: List) -> List # Get vulnerability data
407
session.get_licenses(packages: List) -> Dict # Get license data
408
```
409
410
### Authentication Flow Integration {.api }
411
412
```python
413
from safety.auth.main import get_authorization_data, handle_authentication_flow
414
415
def get_authorization_data(
416
client: SafetyAuthSession, # Client session
417
code_verifier: str, # PKCE code verifier
418
organization: Optional[Organization] = None, # Target organization
419
headless: bool = False # Headless mode flag
420
) -> Tuple[str, Dict]:
421
"""
422
Generate OAuth2 authorization URL and initial state.
423
424
Args:
425
client: Authenticated client session
426
code_verifier: PKCE code verifier for security
427
organization: Target organization for login
428
headless: Enable headless mode for CI/CD
429
430
Returns:
431
Tuple[str, Dict]: Authorization URL and initial state data
432
"""
433
434
def handle_authentication_flow(
435
ctx, # Command context
436
authorization_url: str, # OAuth authorization URL
437
initial_state: Dict, # Initial authentication state
438
headless: bool = False # Headless mode flag
439
) -> bool:
440
"""
441
Handle complete OAuth2 authentication flow.
442
443
Args:
444
ctx: Typer command context
445
authorization_url: URL for user authentication
446
initial_state: Authentication state data
447
headless: Use headless authentication mode
448
449
Returns:
450
bool: True if authentication successful
451
452
Raises:
453
InvalidCredentialError: If authentication fails
454
NetworkConnectionError: If unable to connect to auth server
455
"""
456
```
457
458
## Error Handling { .api }
459
460
### Authentication Exceptions { .api }
461
462
```python
463
from safety.errors import (
464
InvalidCredentialError,
465
NetworkConnectionError,
466
RequestTimeoutError,
467
ServerError,
468
TooManyRequestsError
469
)
470
471
# Authentication-specific errors
472
class InvalidCredentialError(SafetyError):
473
"""Raised when provided credentials are invalid or expired."""
474
475
class NetworkConnectionError(SafetyError):
476
"""Raised when unable to establish network connection."""
477
478
class RequestTimeoutError(SafetyError):
479
"""Raised when requests exceed configured timeout."""
480
```
481
482
### Error Handling Patterns { .api }
483
484
```python
485
from safety.auth.utils import SafetyAuthSession
486
from safety.errors import InvalidCredentialError
487
488
try:
489
session = SafetyAuthSession()
490
session.api_key = "sk-invalid-key"
491
492
response = session.get("https://api.safetycli.com/user/profile")
493
response.raise_for_status()
494
495
except InvalidCredentialError as e:
496
print(f"Authentication failed: {e}")
497
except NetworkConnectionError as e:
498
print(f"Network error: {e}")
499
except RequestTimeoutError as e:
500
print(f"Request timed out: {e}")
501
```
502
503
## Configuration and Environment
504
505
### Environment Variables { .api }
506
507
Authentication behavior can be configured via environment variables:
508
509
```bash
510
# API authentication
511
SAFETY_API_KEY # Default API key
512
SAFETY_API_BASE_URL # API base URL override
513
514
# Proxy configuration
515
SAFETY_PROXY_HOST # Proxy hostname
516
SAFETY_PROXY_PORT # Proxy port
517
SAFETY_PROXY_PROTOCOL # Proxy protocol (http/https)
518
SAFETY_PROXY_TIMEOUT # Proxy timeout in seconds
519
520
# OAuth configuration
521
SAFETY_AUTH_SERVER_URL # Authentication server URL
522
SAFETY_CLIENT_ID # OAuth2 client ID
523
```
524
525
### Configuration Files { .api }
526
527
Authentication settings are stored in Safety configuration directories:
528
529
```bash
530
# User configuration (Linux/macOS)
531
~/.config/safety/auth.json
532
533
# System configuration
534
/etc/safety/auth.json
535
536
# Windows user configuration
537
%APPDATA%\safety\auth.json
538
```
539
540
## Usage Examples
541
542
### Basic Authentication Setup
543
544
```python
545
from safety.auth.cli_utils import build_client_session
546
from safety.auth.models import Organization
547
from safety_schemas.models import Stage
548
549
# API Key Authentication
550
session = build_client_session(api_key="sk-12345abcdef")
551
552
# OAuth Token Authentication (requires prior login)
553
session = build_client_session()
554
555
# Organization-specific Authentication
556
org = Organization(id="123", name="My Company")
557
session = build_client_session(
558
organization=org,
559
stage=Stage.PRODUCTION
560
)
561
```
562
563
### Corporate Proxy Configuration
564
565
```python
566
# Configure session with corporate proxy
567
session = build_client_session(
568
api_key="sk-12345abcdef",
569
proxy_protocol="https",
570
proxy_host="proxy.company.com",
571
proxy_port=8080,
572
proxy_timeout=60
573
)
574
575
# Make authenticated request through proxy
576
response = session.get("https://api.safetycli.com/scan")
577
```
578
579
### Multi-Organization Access
580
581
```python
582
from safety.auth.models import Auth, Organization
583
584
# List available organizations
585
session = build_client_session()
586
orgs_response = session.get("/organizations")
587
organizations = [
588
Organization(id=org["id"], name=org["name"])
589
for org in orgs_response.json()
590
]
591
592
# Switch to specific organization
593
target_org = organizations[0]
594
org_session = build_client_session(organization=target_org)
595
```
596
597
### Authentication State Management
598
599
```python
600
from safety.auth.models import Auth
601
602
# Check authentication status
603
auth = Auth(...) # Initialized from context
604
605
if auth.is_valid():
606
method = auth.get_auth_method()
607
print(f"Authenticated via {method}")
608
609
if auth.org:
610
print(f"Organization: {auth.org.name}")
611
612
if auth.email:
613
print(f"User: {auth.name} ({auth.email})")
614
else:
615
print("Not authenticated")
616
```
617
618
This comprehensive authentication documentation covers all aspects of Safety CLI's authentication system, enabling developers to implement secure and robust integrations with the Safety platform.