0
# Authentication
1
2
Credential management for Git operations supporting various authentication methods including SSH keys, username/password, and SSH agent. Provides secure authentication for remote operations with flexible credential providers.
3
4
## Capabilities
5
6
### Credential Types
7
8
Different credential types for various authentication scenarios.
9
10
```python { .api }
11
class Username:
12
def __init__(self, username: str):
13
"""
14
Username-only credentials.
15
16
Parameters:
17
- username: Username for authentication
18
"""
19
20
class UserPass:
21
def __init__(self, username: str, password: str):
22
"""
23
Username and password credentials.
24
25
Parameters:
26
- username: Username for authentication
27
- password: Password or personal access token
28
"""
29
30
class Keypair:
31
def __init__(
32
self,
33
username: str,
34
pubkey_path: str,
35
privkey_path: str,
36
passphrase: str = ''
37
):
38
"""
39
SSH key pair credentials from files.
40
41
Parameters:
42
- username: Username for SSH authentication (usually 'git')
43
- pubkey_path: Path to public key file
44
- privkey_path: Path to private key file
45
- passphrase: Private key passphrase (empty if none)
46
"""
47
48
class KeypairFromAgent:
49
def __init__(self, username: str):
50
"""
51
SSH key pair credentials from SSH agent.
52
53
Parameters:
54
- username: Username for SSH authentication (usually 'git')
55
"""
56
57
class KeypairFromMemory:
58
def __init__(
59
self,
60
username: str,
61
pubkey_data: str,
62
privkey_data: str,
63
passphrase: str = ''
64
):
65
"""
66
SSH key pair credentials from memory.
67
68
Parameters:
69
- username: Username for SSH authentication
70
- pubkey_data: Public key data as string
71
- privkey_data: Private key data as string
72
- passphrase: Private key passphrase (empty if none)
73
"""
74
```
75
76
### Credential Type Constants
77
78
Constants for identifying credential types in authentication callbacks.
79
80
```python { .api }
81
# Credential Type Flags
82
GIT_CREDENTIAL_USERPASS_PLAINTEXT: int # Username/password
83
GIT_CREDENTIAL_SSH_KEY: int # SSH key pair
84
GIT_CREDENTIAL_SSH_CUSTOM: int # Custom SSH authentication
85
GIT_CREDENTIAL_DEFAULT: int # Default credentials
86
GIT_CREDENTIAL_SSH_INTERACTIVE: int # Interactive SSH
87
GIT_CREDENTIAL_USERNAME: int # Username only
88
GIT_CREDENTIAL_SSH_MEMORY: int # SSH key from memory
89
```
90
91
### Credential Helper Functions
92
93
Utility functions for credential management.
94
95
```python { .api }
96
def get_credentials(
97
url: str,
98
username_from_url: str,
99
allowed_types: int
100
):
101
"""
102
Default credential provider function.
103
104
Parameters:
105
- url: Remote URL requesting credentials
106
- username_from_url: Username extracted from URL
107
- allowed_types: Bitfield of allowed credential types
108
109
Returns:
110
Appropriate credential object or None
111
"""
112
```
113
114
### Authentication in Remote Operations
115
116
Authentication is typically handled through RemoteCallbacks credential method.
117
118
```python { .api }
119
class RemoteCallbacks:
120
def credentials(
121
self,
122
url: str,
123
username_from_url: str,
124
allowed_types: int
125
):
126
"""
127
Provide credentials for remote operation.
128
129
Parameters:
130
- url: Remote URL needing authentication
131
- username_from_url: Username from URL (if any)
132
- allowed_types: Credential types allowed by remote
133
134
Returns:
135
Credential object matching allowed types
136
"""
137
return None
138
139
def certificate_check(
140
self,
141
certificate,
142
valid: bool,
143
host: str
144
) -> bool:
145
"""
146
Verify server certificate for HTTPS.
147
148
Parameters:
149
- certificate: Server certificate
150
- valid: True if certificate passed basic validation
151
- host: Hostname being connected to
152
153
Returns:
154
True to accept certificate, False to reject
155
"""
156
return valid
157
```
158
159
### Usage Examples
160
161
#### SSH Key Authentication
162
163
```python
164
import pygit2
165
166
# SSH key from files
167
class SSHKeyCallbacks(pygit2.RemoteCallbacks):
168
def credentials(self, url, username_from_url, allowed_types):
169
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
170
return pygit2.Keypair(
171
'git',
172
'/home/user/.ssh/id_rsa.pub',
173
'/home/user/.ssh/id_rsa',
174
'my_passphrase' # Empty string if no passphrase
175
)
176
return None
177
178
# Use with remote operations
179
repo = pygit2.Repository('/path/to/repo')
180
callbacks = SSHKeyCallbacks()
181
origin = repo.remotes['origin']
182
origin.fetch(callbacks=callbacks)
183
```
184
185
#### SSH Agent Authentication
186
187
```python
188
# Use SSH agent for key management
189
class SSHAgentCallbacks(pygit2.RemoteCallbacks):
190
def credentials(self, url, username_from_url, allowed_types):
191
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
192
return pygit2.KeypairFromAgent('git')
193
return None
194
195
callbacks = SSHAgentCallbacks()
196
origin.fetch(callbacks=callbacks)
197
```
198
199
#### Username/Password Authentication
200
201
```python
202
# HTTPS with username/password
203
class HTTPSCallbacks(pygit2.RemoteCallbacks):
204
def credentials(self, url, username_from_url, allowed_types):
205
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
206
# For GitHub, use personal access token as password
207
return pygit2.UserPass('username', 'personal_access_token')
208
return None
209
210
callbacks = HTTPSCallbacks()
211
origin.push(['refs/heads/main'], callbacks=callbacks)
212
```
213
214
#### Multi-Method Authentication
215
216
```python
217
# Support multiple authentication methods
218
class FlexibleCallbacks(pygit2.RemoteCallbacks):
219
def __init__(self, ssh_key_path=None, username=None, password=None):
220
self.ssh_key_path = ssh_key_path
221
self.username = username
222
self.password = password
223
224
def credentials(self, url, username_from_url, allowed_types):
225
# Try SSH key first
226
if (allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY and
227
self.ssh_key_path):
228
return pygit2.Keypair(
229
'git',
230
f'{self.ssh_key_path}.pub',
231
self.ssh_key_path,
232
''
233
)
234
235
# Try SSH agent
236
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
237
return pygit2.KeypairFromAgent('git')
238
239
# Try username/password
240
if (allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT and
241
self.username and self.password):
242
return pygit2.UserPass(self.username, self.password)
243
244
# Try username only
245
if (allowed_types & pygit2.GIT_CREDENTIAL_USERNAME and
246
self.username):
247
return pygit2.Username(self.username)
248
249
return None
250
251
# Use flexible authentication
252
callbacks = FlexibleCallbacks(
253
ssh_key_path='/home/user/.ssh/id_rsa',
254
username='myuser',
255
password='mytoken'
256
)
257
```
258
259
#### In-Memory SSH Keys
260
261
```python
262
# Load SSH keys from strings (useful for CI/CD)
263
class MemoryKeyCallbacks(pygit2.RemoteCallbacks):
264
def __init__(self, private_key_data, public_key_data, passphrase=''):
265
self.private_key_data = private_key_data
266
self.public_key_data = public_key_data
267
self.passphrase = passphrase
268
269
def credentials(self, url, username_from_url, allowed_types):
270
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
271
return pygit2.KeypairFromMemory(
272
'git',
273
self.public_key_data,
274
self.private_key_data,
275
self.passphrase
276
)
277
return None
278
279
# Read keys from environment or config
280
import os
281
private_key = os.environ.get('SSH_PRIVATE_KEY')
282
public_key = os.environ.get('SSH_PUBLIC_KEY')
283
284
if private_key and public_key:
285
callbacks = MemoryKeyCallbacks(private_key, public_key)
286
origin.fetch(callbacks=callbacks)
287
```
288
289
#### Certificate Verification
290
291
```python
292
# Custom certificate verification
293
class SecureCallbacks(pygit2.RemoteCallbacks):
294
def certificate_check(self, certificate, valid, host):
295
# Always verify certificates in production
296
if not valid:
297
print(f"Invalid certificate for {host}")
298
return False
299
300
# Additional custom verification
301
if 'github.com' in host:
302
# Accept GitHub certificates
303
return True
304
elif 'company.com' in host:
305
# Custom verification for company servers
306
return self.verify_company_cert(certificate)
307
308
# Default to system validation
309
return valid
310
311
def verify_company_cert(self, certificate):
312
# Custom certificate verification logic
313
return True
314
315
def credentials(self, url, username_from_url, allowed_types):
316
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
317
return pygit2.UserPass('user', 'token')
318
return None
319
```
320
321
#### Environment-Based Authentication
322
323
```python
324
import os
325
326
class EnvironmentCallbacks(pygit2.RemoteCallbacks):
327
def credentials(self, url, username_from_url, allowed_types):
328
# SSH key from environment
329
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
330
ssh_key = os.environ.get('GIT_SSH_KEY')
331
if ssh_key:
332
public_key = ssh_key + '.pub'
333
if os.path.exists(ssh_key) and os.path.exists(public_key):
334
passphrase = os.environ.get('GIT_SSH_PASSPHRASE', '')
335
return pygit2.Keypair('git', public_key, ssh_key, passphrase)
336
337
# Username/password from environment
338
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
339
username = os.environ.get('GIT_USERNAME')
340
password = os.environ.get('GIT_PASSWORD') or os.environ.get('GIT_TOKEN')
341
if username and password:
342
return pygit2.UserPass(username, password)
343
344
return None
345
346
# Use environment-based authentication
347
callbacks = EnvironmentCallbacks()
348
```
349
350
#### Interactive Authentication
351
352
```python
353
import getpass
354
355
class InteractiveCallbacks(pygit2.RemoteCallbacks):
356
def credentials(self, url, username_from_url, allowed_types):
357
print(f"Authentication required for {url}")
358
359
# SSH key with prompt for passphrase
360
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
361
ssh_key = input("SSH private key path [~/.ssh/id_rsa]: ")
362
if not ssh_key:
363
ssh_key = os.path.expanduser('~/.ssh/id_rsa')
364
365
public_key = ssh_key + '.pub'
366
if os.path.exists(ssh_key) and os.path.exists(public_key):
367
passphrase = getpass.getpass("SSH key passphrase (empty if none): ")
368
return pygit2.Keypair('git', public_key, ssh_key, passphrase)
369
370
# Username/password with prompts
371
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
372
username = input(f"Username [{username_from_url}]: ") or username_from_url
373
password = getpass.getpass("Password/Token: ")
374
if username and password:
375
return pygit2.UserPass(username, password)
376
377
return None
378
379
# Note: Only use interactive auth in appropriate contexts
380
callbacks = InteractiveCallbacks()
381
```
382
383
#### Clone with Authentication
384
385
```python
386
# Clone repository with authentication
387
def clone_with_auth(url, path, auth_method='ssh_agent'):
388
if auth_method == 'ssh_agent':
389
callbacks = SSHAgentCallbacks()
390
elif auth_method == 'ssh_key':
391
callbacks = SSHKeyCallbacks()
392
elif auth_method == 'https':
393
callbacks = HTTPSCallbacks()
394
else:
395
callbacks = FlexibleCallbacks()
396
397
try:
398
repo = pygit2.clone_repository(url, path, callbacks=callbacks)
399
print(f"Successfully cloned {url} to {path}")
400
return repo
401
except pygit2.GitError as e:
402
print(f"Clone failed: {e}")
403
return None
404
405
# Clone with different auth methods
406
repo = clone_with_auth('git@github.com:user/repo.git', '/local/path', 'ssh_agent')
407
repo = clone_with_auth('https://github.com/user/repo.git', '/local/path', 'https')
408
```
409
410
#### Authentication Error Handling
411
412
```python
413
class RobustCallbacks(pygit2.RemoteCallbacks):
414
def __init__(self):
415
self.auth_attempts = 0
416
self.max_attempts = 3
417
418
def credentials(self, url, username_from_url, allowed_types):
419
self.auth_attempts += 1
420
421
if self.auth_attempts > self.max_attempts:
422
print("Maximum authentication attempts exceeded")
423
return None
424
425
print(f"Authentication attempt {self.auth_attempts}/{self.max_attempts}")
426
427
# Try different methods based on attempt number
428
if self.auth_attempts == 1 and allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
429
try:
430
return pygit2.KeypairFromAgent('git')
431
except Exception as e:
432
print(f"SSH agent failed: {e}")
433
434
if self.auth_attempts == 2 and allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
435
key_path = os.path.expanduser('~/.ssh/id_rsa')
436
if os.path.exists(key_path):
437
return pygit2.Keypair('git', key_path + '.pub', key_path, '')
438
439
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
440
# Final attempt with username/password
441
username = input("Username: ")
442
password = getpass.getpass("Password: ")
443
return pygit2.UserPass(username, password)
444
445
return None
446
447
callbacks = RobustCallbacks()
448
try:
449
origin.fetch(callbacks=callbacks)
450
except pygit2.GitError as e:
451
if "authentication" in str(e).lower():
452
print("Authentication failed after all attempts")
453
else:
454
print(f"Other error: {e}")
455
```