Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
40033cf
adding new AuthHttpServer class
sfc-gh-mkeller Jan 14, 2025
3b07b8e
unrelated simplifications
sfc-gh-mkeller Jan 14, 2025
385f763
adding new oauth code flow implementation
sfc-gh-mkeller Jan 14, 2025
ac7cded
adding new TODO
sfc-gh-mkeller Jan 14, 2025
2a4456c
adding changelog entry
sfc-gh-mkeller Jan 14, 2025
ee45f2d
making oauth state random
sfc-gh-mkeller Jan 14, 2025
f39e952
make client_secret optional
sfc-gh-mkeller Jan 15, 2025
8fde1fc
make state check failure fail auth
sfc-gh-mkeller Jan 15, 2025
6d025e5
switch to cryptographically-secure source
sfc-gh-mkeller Jan 16, 2025
54e7833
redo connection parameters based on feedback
sfc-gh-mkeller Jan 24, 2025
d8edbf7
some string updates
sfc-gh-mkeller Jan 27, 2025
5275879
add Authorization header to token request
sfc-gh-mkeller Jan 27, 2025
4d71f0c
fix logging bug
sfc-gh-mkeller Jan 29, 2025
a887c29
review feedback
sfc-gh-mkeller Jan 29, 2025
a20dd97
rewording parameters based on discussion
sfc-gh-mkeller Jan 29, 2025
4d78183
reworking the defaut oauth scope
sfc-gh-mkeller Jan 29, 2025
60b461a
adding port support into oauth code flow auth
sfc-gh-mkeller Jan 29, 2025
f9214a7
implement PKCE
sfc-gh-mkeller Jan 16, 2025
81627af
review feedback
sfc-gh-mkeller Jan 30, 2025
adc1380
adding new AuthHttpServer class
sfc-gh-mkeller Jan 14, 2025
cdc3fc0
unrelated simplifications
sfc-gh-mkeller Jan 14, 2025
7dfd201
adding new oauth code flow implementation
sfc-gh-mkeller Jan 14, 2025
c9d0453
adding new TODO
sfc-gh-mkeller Jan 14, 2025
0c6841a
adding changelog entry
sfc-gh-mkeller Jan 14, 2025
2ff5e8f
making oauth state random
sfc-gh-mkeller Jan 14, 2025
4d8d214
make client_secret optional
sfc-gh-mkeller Jan 15, 2025
a8edcb7
make state check failure fail auth
sfc-gh-mkeller Jan 15, 2025
38564b1
switch to cryptographically-secure source
sfc-gh-mkeller Jan 16, 2025
24308f3
redo connection parameters based on feedback
sfc-gh-mkeller Jan 24, 2025
1ad5185
some string updates
sfc-gh-mkeller Jan 27, 2025
301438a
add Authorization header to token request
sfc-gh-mkeller Jan 27, 2025
fccfcb2
fix logging bug
sfc-gh-mkeller Jan 29, 2025
8dbfdf0
review feedback
sfc-gh-mkeller Jan 29, 2025
c07689b
rewording parameters based on discussion
sfc-gh-mkeller Jan 29, 2025
eeda415
reworking the defaut oauth scope
sfc-gh-mkeller Jan 29, 2025
ea0bff2
adding port support into oauth code flow auth
sfc-gh-mkeller Jan 29, 2025
6b3bd63
do not log state
sfc-gh-mkeller Jan 31, 2025
a850492
Update src/snowflake/connector/connection.py
sfc-gh-mkeller Feb 3, 2025
d5de908
Merge branch 'mkeller/SNOW-1825621/oauth-code-flow-support' into mkel…
sfc-gh-mmishchenko Feb 12, 2025
70f1ce1
adding new AuthHttpServer class
sfc-gh-mkeller Jan 14, 2025
85937f1
unrelated simplifications
sfc-gh-mkeller Jan 14, 2025
f2e0f6e
adding new oauth code flow implementation
sfc-gh-mkeller Jan 14, 2025
3649a20
adding new TODO
sfc-gh-mkeller Jan 14, 2025
fa99a3c
adding changelog entry
sfc-gh-mkeller Jan 14, 2025
33dc5af
making oauth state random
sfc-gh-mkeller Jan 14, 2025
60fb3db
make client_secret optional
sfc-gh-mkeller Jan 15, 2025
df0d8e5
make state check failure fail auth
sfc-gh-mkeller Jan 15, 2025
9fa8b01
switch to cryptographically-secure source
sfc-gh-mkeller Jan 16, 2025
a743a81
redo connection parameters based on feedback
sfc-gh-mkeller Jan 24, 2025
43c2e4f
some string updates
sfc-gh-mkeller Jan 27, 2025
7434b85
add Authorization header to token request
sfc-gh-mkeller Jan 27, 2025
b08f346
fix logging bug
sfc-gh-mkeller Jan 29, 2025
5d2d602
review feedback
sfc-gh-mkeller Jan 29, 2025
e265a51
rewording parameters based on discussion
sfc-gh-mkeller Jan 29, 2025
f187ad5
reworking the defaut oauth scope
sfc-gh-mkeller Jan 29, 2025
ccb6684
adding port support into oauth code flow auth
sfc-gh-mkeller Jan 29, 2025
3b948b4
do not log state
sfc-gh-mkeller Jan 31, 2025
3fbb1ed
Update src/snowflake/connector/connection.py
sfc-gh-mkeller Feb 3, 2025
8bbbc55
Merge branch 'mkeller/SNOW-1825621/oauth-code-flow-support' into mkel…
sfc-gh-mmishchenko Feb 19, 2025
6e76857
adding new AuthHttpServer class
sfc-gh-mkeller Jan 14, 2025
c6c17d1
unrelated simplifications
sfc-gh-mkeller Jan 14, 2025
550e54c
adding new oauth code flow implementation
sfc-gh-mkeller Jan 14, 2025
f9ffa01
adding new TODO
sfc-gh-mkeller Jan 14, 2025
ff44946
adding changelog entry
sfc-gh-mkeller Jan 14, 2025
a1d9b54
making oauth state random
sfc-gh-mkeller Jan 14, 2025
fec1dca
make client_secret optional
sfc-gh-mkeller Jan 15, 2025
ff041a2
make state check failure fail auth
sfc-gh-mkeller Jan 15, 2025
3e60038
switch to cryptographically-secure source
sfc-gh-mkeller Jan 16, 2025
a85abac
redo connection parameters based on feedback
sfc-gh-mkeller Jan 24, 2025
e4549ff
some string updates
sfc-gh-mkeller Jan 27, 2025
1305df7
add Authorization header to token request
sfc-gh-mkeller Jan 27, 2025
fc6a403
fix logging bug
sfc-gh-mkeller Jan 29, 2025
638f741
review feedback
sfc-gh-mkeller Jan 29, 2025
b94eeb7
rewording parameters based on discussion
sfc-gh-mkeller Jan 29, 2025
e7e664e
reworking the defaut oauth scope
sfc-gh-mkeller Jan 29, 2025
725b3d0
adding port support into oauth code flow auth
sfc-gh-mkeller Jan 29, 2025
6b9b48c
do not log state
sfc-gh-mkeller Jan 31, 2025
acfe7e7
Update src/snowflake/connector/connection.py
sfc-gh-mkeller Feb 3, 2025
6bad2e1
SNOW-1917265: Add OAUTH_TYPE for authorization flow (#2174)
sfc-gh-pbulawa Feb 25, 2025
91eb76e
Merge branch 'mkeller/SNOW-1825621/oauth-code-flow-support' into mkel…
sfc-gh-mmishchenko Feb 25, 2025
a83d09c
Merge branch 'mkeller/SNOW-1825621/oauth-code-flow-support' into mkel…
sfc-gh-mmishchenko Mar 4, 2025
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
2 changes: 2 additions & 0 deletions DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
- Updated the log level for cursor's chunk rowcount from INFO to DEBUG.
- Added a feature to verify if the connection is still good enough to send queries over.
- Added support for base64-encoded DER private key strings in the `private_key` authentication type.
- Added support for OAuth authorization code flow.
- Added support for PKCE on top of OAuth authorization flow.

- v3.12.4(December 3,2024)
- Fixed a bug where multipart uploads to Azure would be missing their MD5 hashes.
Expand Down
3 changes: 3 additions & 0 deletions src/snowflake/connector/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .idtoken import AuthByIdToken
from .keypair import AuthByKeyPair
from .oauth import AuthByOAuth
from .oauth_code import AuthByOauthCode
from .okta import AuthByOkta
from .pat import AuthByPAT
from .usrpwdmfa import AuthByUsrPwdMfa
Expand All @@ -20,6 +21,7 @@
AuthByDefault,
AuthByKeyPair,
AuthByOAuth,
AuthByOauthCode,
AuthByOkta,
AuthByUsrPwdMfa,
AuthByWebBrowser,
Expand All @@ -34,6 +36,7 @@
"AuthByKeyPair",
"AuthByPAT",
"AuthByOAuth",
"AuthByOauthCode",
"AuthByOkta",
"AuthByUsrPwdMfa",
"AuthByWebBrowser",
Expand Down
128 changes: 128 additions & 0 deletions src/snowflake/connector/auth/_http_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
#

from __future__ import annotations

import logging
import os
import select
import socket
import time

from ..compat import IS_WINDOWS
from ..errorcode import ER_NO_HOSTNAME_FOUND
from ..errors import OperationalError

logger = logging.getLogger(__name__)


class AuthHttpServer:
"""Simple HTTP server to receive callbacks through for auth purposes."""

def __init__(
self,
hostname: str = "localhost",
buf_size: int = 16384,
) -> None:
self.buf_size = buf_size
self._socket_connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

if os.getenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "False").lower() == "true":
if IS_WINDOWS:
logger.warning(
"Configuration SNOWFLAKE_AUTH_SOCKET_REUSE_PORT is not available in Windows. Ignoring."
)
else:
self._socket_connection.setsockopt(
socket.SOL_SOCKET, socket.SO_REUSEPORT, 1
)

try:
self._socket_connection.bind(
(
os.getenv("SF_AUTH_SOCKET_ADDR", hostname),
int(os.getenv("SF_AUTH_SOCKET_PORT", 0)),
)
)
except socket.gaierror as ex:
if ex.args[0] == socket.EAI_NONAME and hostname == "localhost":
raise OperationalError(
msg="localhost is not found. Ensure /etc/hosts has "
"localhost entry.",
errno=ER_NO_HOSTNAME_FOUND,
)
raise
try:
self._socket_connection.listen(0) # no backlog
self.port = self._socket_connection.getsockname()[1]
except Exception:
self._socket_connection.close()

def receive_block(
self,
max_attempts: int = 15,
) -> tuple[list[str], socket.socket]:
"""Receive a message with a maximum attempt count, blocking."""
socket_client = None
while True:
try:
attempts = 0
raw_data = bytearray()

msg_dont_wait = (
os.getenv("SNOWFLAKE_AUTH_SOCKET_MSG_DONTWAIT", "false").lower()
== "true"
)
if IS_WINDOWS:
if msg_dont_wait:
logger.warning(
"Configuration SNOWFLAKE_AUTH_SOCKET_MSG_DONTWAIT is not available in Windows. Ignoring."
)
msg_dont_wait = False

# when running in a containerized environment, socket_client.recv ocassionally returns an empty byte array
# an immediate successive call to socket_client.recv gets the actual data
while len(raw_data) == 0 and attempts < max_attempts:
attempts += 1
read_sockets, _write_sockets, _exception_sockets = select.select(
[self._socket_connection], [], []
)

if read_sockets[0] is not None:
# Receive the data in small chunks and retransmit it
socket_client, _ = self._socket_connection.accept()

try:
if msg_dont_wait:
# WSL containerized environment sometimes causes socket_client.recv to hang indefinetly
# To avoid this, passing the socket.MSG_DONTWAIT flag which raises BlockingIOError if
# operation would block
logger.debug(
"Calling socket_client.recv with MSG_DONTWAIT flag due to SNOWFLAKE_AUTH_SOCKET_MSG_DONTWAIT env var"
)
raw_data = socket_client.recv(
BUF_SIZE, socket.MSG_DONTWAIT
)
else:
raw_data = socket_client.recv(self.buf_size)

except BlockingIOError:
logger.debug(
"BlockingIOError raised from socket.recv while attempting to retrieve callback request"
)
if attempts < max_attempts:
sleep_time = 0.25
logger.debug(
f"Waiting {sleep_time} seconds before trying again"
)
time.sleep(sleep_time)
else:
logger.debug("Exceeded retry count")

assert socket_client is not None
return raw_data.decode("utf-8").split("\r\n"), socket_client
except Exception:
if socket_client is not None:
socket_client.shutdown(socket.SHUT_RDWR)
socket_client.close()
Loading
Loading