56 lines
1.5 KiB
Markdown
56 lines
1.5 KiB
Markdown
# IMAP Quoted-Printable JWT Token Extraction
|
|
|
|
## Problem
|
|
Verification emails often contain JWT tokens in URLs that are split across multiple lines due to quoted-printable encoding. The `=` at end of line is a soft break.
|
|
|
|
## Example
|
|
|
|
Raw email content:
|
|
```
|
|
token=[REDACTED]
|
|
eyJlbWFpbCI6Im...\r\n
|
|
3ODQ3NDM1Mzd9.qiOoGUNZZsPse1jQnZ8JHvUrb6xKX5KbPatfwvXUPGA&callbackURL=3D%2F
|
|
```
|
|
|
|
## Extraction Code
|
|
|
|
```python
|
|
import imaplib, re
|
|
|
|
M = imaplib.IMAP4_SSL('imap.gmail.com')
|
|
M.login(email, app_password)
|
|
M.select('INBOX')
|
|
typ, data = M.search(None, 'FROM', 'sender')
|
|
ids = data[0].split() if data[0] else []
|
|
typ, msg = M.fetch(ids[-1], '(RFC822)')
|
|
raw = msg[0][1]
|
|
|
|
# Find token= in raw bytes
|
|
idx = raw.find(b'token=')
|
|
chunk = raw[idx+6:idx+250] # skip 'token='
|
|
|
|
# Remove soft breaks (=\r\n)
|
|
result = []
|
|
i = 0
|
|
while i < len(chunk):
|
|
if chunk[i:i+1] == b'&':
|
|
break
|
|
if i+2 < len(chunk) and chunk[i:i+3] == b'=\r\n':
|
|
i += 3 # skip soft break
|
|
elif i+1 < len(chunk) and chunk[i:i+1] == b'=' and chunk[i+1:i+2] == b'\r':
|
|
i += 3
|
|
else:
|
|
result.append(chr(chunk[i]))
|
|
i += 1
|
|
|
|
token = ''.join(result)
|
|
# Remove 3D prefix if present (= in quoted-printable)
|
|
if token.startswith('3D'):
|
|
token = [REDACTED]]
|
|
```
|
|
|
|
## Important
|
|
- Python's `repr()` truncates long strings with `...` — the actual string is complete
|
|
- The `3D` prefix is the quoted-printable encoding of `=`
|
|
- Always work with raw bytes, not decoded text, to avoid encoding issues
|
|
- Use `b'...'` byte strings for searching in raw email content |