2025-05-15 19:49:42 -07:00
import structlog
from fastapi import Body , Depends , HTTPException , Path , Query
from skyvern . forge import app
from skyvern . forge . prompts import prompt_engine
2025-08-05 07:34:26 -07:00
from skyvern . forge . sdk . db . enums import OrganizationAuthTokenType
2025-05-18 13:45:46 -07:00
from skyvern . forge . sdk . routes . code_samples import (
CREATE_CREDENTIAL_CODE_SAMPLE ,
CREATE_CREDENTIAL_CODE_SAMPLE_CREDIT_CARD ,
DELETE_CREDENTIAL_CODE_SAMPLE ,
GET_CREDENTIAL_CODE_SAMPLE ,
GET_CREDENTIALS_CODE_SAMPLE ,
SEND_TOTP_CODE_CODE_SAMPLE ,
)
2025-05-15 19:49:42 -07:00
from skyvern . forge . sdk . routes . routers import base_router , legacy_base_router
from skyvern . forge . sdk . schemas . credentials import (
CreateCredentialRequest ,
CredentialResponse ,
CredentialType ,
CreditCardCredentialResponse ,
PasswordCredentialResponse ,
)
2025-08-05 07:34:26 -07:00
from skyvern . forge . sdk . schemas . organizations import (
2025-09-23 10:16:48 -06:00
AzureClientSecretCredentialResponse ,
CreateAzureClientSecretCredentialRequest ,
2025-08-05 07:34:26 -07:00
CreateOnePasswordTokenRequest ,
CreateOnePasswordTokenResponse ,
Organization ,
)
2025-05-15 19:49:42 -07:00
from skyvern . forge . sdk . schemas . totp_codes import TOTPCode , TOTPCodeCreate
from skyvern . forge . sdk . services import org_auth_service
from skyvern . forge . sdk . services . bitwarden import BitwardenService
LOG = structlog . get_logger ( )
2025-09-11 18:10:05 -07:00
async def parse_totp_code ( content : str , organization_id : str ) - > str | None :
2025-05-18 12:43:22 -07:00
prompt = prompt_engine . load_prompt ( " parse-verification-code " , content = content )
2025-09-11 18:10:05 -07:00
code_resp = await app . SECONDARY_LLM_API_HANDLER (
prompt = prompt , prompt_name = " parse-verification-code " , organization_id = organization_id
)
2025-06-03 15:15:51 -07:00
LOG . info ( " TOTP Code Parser Response " , code_resp = code_resp )
2025-05-18 12:43:22 -07:00
return code_resp . get ( " code " , None )
2025-07-15 02:01:23 -07:00
@legacy_base_router.post ( " /totp " )
2025-05-15 19:49:42 -07:00
@legacy_base_router.post ( " /totp/ " , include_in_schema = False )
@base_router.post (
" /credentials/totp " ,
response_model = TOTPCode ,
2025-07-15 02:05:33 -07:00
summary = " Send TOTP code " ,
2025-05-21 20:06:34 -07:00
description = " Forward a TOTP (2FA, MFA) email or sms message containing the code to Skyvern. This endpoint stores the code in database so that Skyvern can use it while running tasks/workflows. " ,
2025-05-15 19:49:42 -07:00
tags = [ " Credentials " ] ,
openapi_extra = {
" x-fern-sdk-method-name " : " send_totp_code " ,
2025-07-15 02:01:23 -07:00
" x-fern-examples " : [ { " code-samples " : [ { " sdk " : " python " , " code " : SEND_TOTP_CODE_CODE_SAMPLE } ] } ] ,
2025-05-15 19:49:42 -07:00
} ,
)
2025-05-18 13:45:46 -07:00
@base_router.post (
" /credentials/totp/ " ,
response_model = TOTPCode ,
include_in_schema = False ,
)
2025-05-15 19:49:42 -07:00
async def send_totp_code (
2025-09-11 18:10:05 -07:00
data : TOTPCodeCreate ,
curr_org : Organization = Depends ( org_auth_service . get_current_org ) ,
2025-05-15 19:49:42 -07:00
) - > TOTPCode :
LOG . info (
" Saving TOTP code " ,
organization_id = curr_org . organization_id ,
totp_identifier = data . totp_identifier ,
task_id = data . task_id ,
workflow_id = data . workflow_id ,
2025-07-17 21:36:18 -07:00
workflow_run_id = data . workflow_run_id ,
2025-05-15 19:49:42 -07:00
)
2025-05-26 13:28:20 -07:00
content = data . content . strip ( )
code : str | None = content
# We assume the user is sending the code directly when the length of code is less than or equal to 10
if len ( content ) > 10 :
2025-09-11 18:10:05 -07:00
code = await parse_totp_code ( content , curr_org . organization_id )
2025-05-15 19:49:42 -07:00
if not code :
2025-05-26 13:28:20 -07:00
LOG . error (
" Failed to parse totp code " ,
totp_identifier = data . totp_identifier ,
task_id = data . task_id ,
workflow_id = data . workflow_id ,
workflow_run_id = data . workflow_run_id ,
content = data . content ,
)
2025-05-15 19:49:42 -07:00
raise HTTPException ( status_code = 400 , detail = " Failed to parse totp code " )
return await app . DATABASE . create_totp_code (
organization_id = curr_org . organization_id ,
totp_identifier = data . totp_identifier ,
content = data . content ,
code = code ,
task_id = data . task_id ,
workflow_id = data . workflow_id ,
workflow_run_id = data . workflow_run_id ,
source = data . source ,
expired_at = data . expired_at ,
)
2025-05-18 12:43:22 -07:00
@legacy_base_router.post ( " /credentials " )
@legacy_base_router.post ( " /credentials/ " , include_in_schema = False )
@base_router.post (
2025-05-15 19:49:42 -07:00
" /credentials " ,
response_model = CredentialResponse ,
2025-05-18 12:43:22 -07:00
status_code = 201 ,
summary = " Create credential " ,
description = " Creates a new credential for the current organization " ,
2025-05-15 19:49:42 -07:00
tags = [ " Credentials " ] ,
openapi_extra = {
2025-05-18 12:43:22 -07:00
" x-fern-sdk-method-name " : " create_credential " ,
2025-05-18 13:45:46 -07:00
" x-fern-examples " : [
{
" code-samples " : [
{ " sdk " : " python " , " code " : CREATE_CREDENTIAL_CODE_SAMPLE } ,
{ " sdk " : " python " , " code " : CREATE_CREDENTIAL_CODE_SAMPLE_CREDIT_CARD } ,
]
}
] ,
2025-05-15 19:49:42 -07:00
} ,
)
2025-05-18 13:45:46 -07:00
@base_router.post (
" /credentials/ " ,
response_model = CredentialResponse ,
status_code = 201 ,
include_in_schema = False ,
)
2025-05-18 12:43:22 -07:00
async def create_credential (
data : CreateCredentialRequest = Body (
2025-05-15 19:49:42 -07:00
. . . ,
2025-05-18 12:43:22 -07:00
description = " The credential data to create " ,
example = {
" name " : " My Credential " ,
" credential_type " : " PASSWORD " ,
" credential " : { " username " : " user@example.com " , " password " : " securepassword123 " , " totp " : " JBSWY3DPEHPK3PXP " } ,
} ,
openapi_extra = { " x-fern-sdk-parameter-name " : " data " } ,
2025-05-15 19:49:42 -07:00
) ,
current_org : Organization = Depends ( org_auth_service . get_current_org ) ,
) - > CredentialResponse :
2025-05-18 12:43:22 -07:00
org_collection = await app . DATABASE . get_organization_bitwarden_collection ( current_org . organization_id )
2025-05-15 19:49:42 -07:00
2025-05-18 12:43:22 -07:00
if not org_collection :
LOG . info (
" There is no collection for the organization. Creating new collection. " ,
organization_id = current_org . organization_id ,
)
collection_id = await BitwardenService . create_collection (
name = current_org . organization_id ,
)
org_collection = await app . DATABASE . create_organization_bitwarden_collection (
current_org . organization_id ,
collection_id ,
)
item_id = await BitwardenService . create_credential_item (
collection_id = org_collection . collection_id ,
name = data . name ,
credential = data . credential ,
2025-05-15 19:49:42 -07:00
)
2025-05-18 12:43:22 -07:00
credential = await app . DATABASE . create_credential (
organization_id = current_org . organization_id ,
item_id = item_id ,
name = data . name ,
credential_type = data . credential_type ,
)
2025-05-15 19:49:42 -07:00
2025-05-18 12:43:22 -07:00
if data . credential_type == CredentialType . PASSWORD :
2025-05-15 19:49:42 -07:00
credential_response = PasswordCredentialResponse (
2025-05-18 12:43:22 -07:00
username = data . credential . username ,
2025-05-15 19:49:42 -07:00
)
return CredentialResponse (
credential = credential_response ,
credential_id = credential . credential_id ,
2025-05-18 12:43:22 -07:00
credential_type = data . credential_type ,
name = data . name ,
2025-05-15 19:49:42 -07:00
)
2025-05-18 12:43:22 -07:00
elif data . credential_type == CredentialType . CREDIT_CARD :
2025-05-15 19:49:42 -07:00
credential_response = CreditCardCredentialResponse (
2025-05-18 12:43:22 -07:00
last_four = data . credential . card_number [ - 4 : ] ,
brand = data . credential . card_brand ,
2025-05-15 19:49:42 -07:00
)
return CredentialResponse (
credential = credential_response ,
credential_id = credential . credential_id ,
2025-05-18 12:43:22 -07:00
credential_type = data . credential_type ,
name = data . name ,
2025-05-15 19:49:42 -07:00
)
@legacy_base_router.delete ( " /credentials/ {credential_id} " )
@legacy_base_router.delete ( " /credentials/ {credential_id} / " , include_in_schema = False )
@base_router.post (
" /credentials/ {credential_id} /delete " ,
status_code = 204 ,
summary = " Delete credential " ,
description = " Deletes a specific credential by its ID " ,
tags = [ " Credentials " ] ,
openapi_extra = {
" x-fern-sdk-method-name " : " delete_credential " ,
2025-05-18 13:45:46 -07:00
" x-fern-examples " : [ { " code-samples " : [ { " sdk " : " python " , " code " : DELETE_CREDENTIAL_CODE_SAMPLE } ] } ] ,
2025-05-15 19:49:42 -07:00
} ,
)
2025-05-18 13:45:46 -07:00
@base_router.post (
" /credentials/ {credential_id} /delete/ " ,
status_code = 204 ,
include_in_schema = False ,
)
2025-05-15 19:49:42 -07:00
async def delete_credential (
credential_id : str = Path (
. . . ,
description = " The unique identifier of the credential to delete " ,
2025-06-13 22:58:55 -07:00
examples = [ " cred_1234567890 " ] ,
2025-05-15 19:49:42 -07:00
openapi_extra = { " x-fern-sdk-parameter-name " : " credential_id " } ,
) ,
current_org : Organization = Depends ( org_auth_service . get_current_org ) ,
) - > None :
organization_bitwarden_collection = await app . DATABASE . get_organization_bitwarden_collection (
current_org . organization_id
)
if not organization_bitwarden_collection :
raise HTTPException ( status_code = 404 , detail = " Credential account not found. It might have been deleted. " )
credential = await app . DATABASE . get_credential (
credential_id = credential_id , organization_id = current_org . organization_id
)
if not credential :
raise HTTPException ( status_code = 404 , detail = f " Credential not found, credential_id= { credential_id } " )
await app . DATABASE . delete_credential ( credential . credential_id , current_org . organization_id )
await BitwardenService . delete_credential_item ( credential . item_id )
return None
2025-05-18 12:43:22 -07:00
@legacy_base_router.get ( " /credentials/ {credential_id} " )
@legacy_base_router.get ( " /credentials/ {credential_id} / " , include_in_schema = False )
@base_router.get (
" /credentials/ {credential_id} " ,
2025-05-15 19:49:42 -07:00
response_model = CredentialResponse ,
2025-05-18 12:43:22 -07:00
summary = " Get credential by ID " ,
description = " Retrieves a specific credential by its ID " ,
2025-05-15 19:49:42 -07:00
tags = [ " Credentials " ] ,
openapi_extra = {
2025-05-18 12:43:22 -07:00
" x-fern-sdk-method-name " : " get_credential " ,
2025-05-18 13:45:46 -07:00
" x-fern-examples " : [ { " code-samples " : [ { " sdk " : " python " , " code " : GET_CREDENTIAL_CODE_SAMPLE } ] } ] ,
2025-05-15 19:49:42 -07:00
} ,
)
2025-05-18 13:45:46 -07:00
@base_router.get (
" /credentials/ {credential_id} / " ,
response_model = CredentialResponse ,
include_in_schema = False ,
)
2025-05-18 12:43:22 -07:00
async def get_credential (
credential_id : str = Path (
2025-05-15 19:49:42 -07:00
. . . ,
2025-05-18 12:43:22 -07:00
description = " The unique identifier of the credential " ,
2025-06-13 22:58:55 -07:00
examples = [ " cred_1234567890 " ] ,
2025-05-18 12:43:22 -07:00
openapi_extra = { " x-fern-sdk-parameter-name " : " credential_id " } ,
2025-05-15 19:49:42 -07:00
) ,
current_org : Organization = Depends ( org_auth_service . get_current_org ) ,
) - > CredentialResponse :
2025-05-18 12:43:22 -07:00
organization_bitwarden_collection = await app . DATABASE . get_organization_bitwarden_collection (
current_org . organization_id
2025-05-15 19:49:42 -07:00
)
2025-05-18 12:43:22 -07:00
if not organization_bitwarden_collection :
raise HTTPException ( status_code = 404 , detail = " Credential account not found. It might have been deleted. " )
2025-05-15 19:49:42 -07:00
2025-05-18 12:43:22 -07:00
credential = await app . DATABASE . get_credential (
credential_id = credential_id , organization_id = current_org . organization_id
2025-05-15 19:49:42 -07:00
)
2025-05-18 12:43:22 -07:00
if not credential :
raise HTTPException ( status_code = 404 , detail = " Credential not found " )
2025-05-15 19:49:42 -07:00
2025-05-18 12:43:22 -07:00
credential_item = await BitwardenService . get_credential_item ( credential . item_id )
if not credential_item :
raise HTTPException ( status_code = 404 , detail = " Credential not found " )
if credential_item . credential_type == CredentialType . PASSWORD :
2025-05-15 19:49:42 -07:00
credential_response = PasswordCredentialResponse (
2025-05-18 12:43:22 -07:00
username = credential_item . credential . username ,
2025-05-15 19:49:42 -07:00
)
return CredentialResponse (
credential = credential_response ,
credential_id = credential . credential_id ,
2025-05-18 12:43:22 -07:00
credential_type = credential_item . credential_type ,
name = credential_item . name ,
2025-05-15 19:49:42 -07:00
)
2025-05-18 12:43:22 -07:00
if credential_item . credential_type == CredentialType . CREDIT_CARD :
2025-05-15 19:49:42 -07:00
credential_response = CreditCardCredentialResponse (
2025-05-18 12:43:22 -07:00
last_four = credential_item . credential . card_number [ - 4 : ] ,
brand = credential_item . credential . card_brand ,
2025-05-15 19:49:42 -07:00
)
return CredentialResponse (
credential = credential_response ,
credential_id = credential . credential_id ,
2025-05-18 12:43:22 -07:00
credential_type = credential_item . credential_type ,
name = credential_item . name ,
2025-05-15 19:49:42 -07:00
)
2025-05-18 12:43:22 -07:00
raise HTTPException ( status_code = 400 , detail = " Invalid credential type " )
@legacy_base_router.get ( " /credentials " )
@legacy_base_router.get ( " /credentials/ " , include_in_schema = False )
@base_router.get (
" /credentials " ,
response_model = list [ CredentialResponse ] ,
summary = " Get all credentials " ,
description = " Retrieves a paginated list of credentials for the current organization " ,
tags = [ " Credentials " ] ,
openapi_extra = {
" x-fern-sdk-method-name " : " get_credentials " ,
2025-05-18 13:45:46 -07:00
" x-fern-examples " : [ { " code-samples " : [ { " sdk " : " python " , " code " : GET_CREDENTIALS_CODE_SAMPLE } ] } ] ,
2025-05-18 12:43:22 -07:00
} ,
)
2025-05-18 13:45:46 -07:00
@base_router.get (
" /credentials/ " ,
response_model = list [ CredentialResponse ] ,
include_in_schema = False ,
)
2025-05-18 12:43:22 -07:00
async def get_credentials (
current_org : Organization = Depends ( org_auth_service . get_current_org ) ,
page : int = Query (
1 ,
ge = 1 ,
description = " Page number for pagination " ,
2025-06-13 22:58:55 -07:00
examples = [ 1 ] ,
2025-05-18 12:43:22 -07:00
openapi_extra = { " x-fern-sdk-parameter-name " : " page " } ,
) ,
page_size : int = Query (
10 ,
ge = 1 ,
description = " Number of items per page " ,
2025-06-13 22:58:55 -07:00
examples = [ 10 ] ,
2025-05-18 12:43:22 -07:00
openapi_extra = { " x-fern-sdk-parameter-name " : " page_size " } ,
) ,
) - > list [ CredentialResponse ] :
organization_bitwarden_collection = await app . DATABASE . get_organization_bitwarden_collection (
current_org . organization_id
)
if not organization_bitwarden_collection :
return [ ]
credentials = await app . DATABASE . get_credentials ( current_org . organization_id , page = page , page_size = page_size )
items = await BitwardenService . get_collection_items ( organization_bitwarden_collection . collection_id )
response_items = [ ]
for credential in credentials :
item = next ( ( item for item in items if item . item_id == credential . item_id ) , None )
if not item :
continue
if item . credential_type == CredentialType . PASSWORD :
credential_response = PasswordCredentialResponse ( username = item . credential . username )
response_items . append (
CredentialResponse (
credential = credential_response ,
credential_id = credential . credential_id ,
credential_type = item . credential_type ,
name = item . name ,
)
)
elif item . credential_type == CredentialType . CREDIT_CARD :
credential_response = CreditCardCredentialResponse (
last_four = item . credential . card_number [ - 4 : ] ,
brand = item . credential . card_brand ,
)
response_items . append (
CredentialResponse (
credential = credential_response ,
credential_id = credential . credential_id ,
credential_type = item . credential_type ,
name = item . name ,
)
)
return response_items
2025-08-05 07:34:26 -07:00
@base_router.get (
2025-08-05 23:33:08 +08:00
" /credentials/onepassword/get " ,
2025-08-05 07:34:26 -07:00
response_model = CreateOnePasswordTokenResponse ,
summary = " Get OnePassword service account token " ,
description = " Retrieves the current OnePassword service account token for the organization. " ,
2025-08-14 09:08:47 -07:00
include_in_schema = False ,
2025-08-05 07:34:26 -07:00
)
@base_router.get (
2025-08-05 23:33:08 +08:00
" /credentials/onepassword/get/ " ,
2025-08-05 07:34:26 -07:00
response_model = CreateOnePasswordTokenResponse ,
include_in_schema = False ,
)
async def get_onepassword_token (
current_org : Organization = Depends ( org_auth_service . get_current_org ) ,
) - > CreateOnePasswordTokenResponse :
"""
Get the current OnePassword service account token for the organization .
"""
try :
auth_token = await app . DATABASE . get_valid_org_auth_token (
organization_id = current_org . organization_id ,
2025-09-26 16:35:47 -07:00
token_type = OrganizationAuthTokenType . onepassword_service_account . value ,
2025-08-05 07:34:26 -07:00
)
if not auth_token :
raise HTTPException (
status_code = 404 ,
detail = " No OnePassword service account token found for this organization " ,
)
return CreateOnePasswordTokenResponse ( token = auth_token )
except HTTPException :
raise
except Exception as e :
LOG . error (
" Failed to get OnePassword service account token " ,
organization_id = current_org . organization_id ,
error = str ( e ) ,
exc_info = True ,
)
raise HTTPException (
status_code = 500 ,
detail = f " Failed to get OnePassword service account token: { str ( e ) } " ,
)
@base_router.post (
2025-08-05 23:33:08 +08:00
" /credentials/onepassword/create " ,
2025-08-05 07:34:26 -07:00
response_model = CreateOnePasswordTokenResponse ,
summary = " Create or update OnePassword service account token " ,
description = " Creates or updates a OnePassword service account token for the current organization. Only one valid token is allowed per organization. " ,
2025-08-14 09:08:47 -07:00
include_in_schema = False ,
2025-08-05 07:34:26 -07:00
)
@base_router.post (
2025-08-05 23:33:08 +08:00
" /credentials/onepassword/create/ " ,
2025-08-05 07:34:26 -07:00
response_model = CreateOnePasswordTokenResponse ,
include_in_schema = False ,
)
async def update_onepassword_token (
2025-08-14 08:29:13 -07:00
data : CreateOnePasswordTokenRequest ,
2025-08-05 07:34:26 -07:00
current_org : Organization = Depends ( org_auth_service . get_current_org ) ,
) - > CreateOnePasswordTokenResponse :
"""
Create or update a OnePassword service account token for the current organization .
This endpoint ensures only one valid OnePassword token exists per organization .
If a valid token already exists , it will be invalidated before creating the new one .
"""
try :
# Invalidate any existing valid OnePassword tokens for this organization
await app . DATABASE . invalidate_org_auth_tokens (
organization_id = current_org . organization_id ,
token_type = OrganizationAuthTokenType . onepassword_service_account ,
)
# Create the new token
auth_token = await app . DATABASE . create_org_auth_token (
organization_id = current_org . organization_id ,
token_type = OrganizationAuthTokenType . onepassword_service_account ,
token = data . token ,
)
LOG . info (
" Created or updated OnePassword service account token " ,
organization_id = current_org . organization_id ,
token_id = auth_token . id ,
)
return CreateOnePasswordTokenResponse ( token = auth_token )
except Exception as e :
LOG . error (
" Failed to create or update OnePassword service account token " ,
organization_id = current_org . organization_id ,
error = str ( e ) ,
exc_info = True ,
)
raise HTTPException (
status_code = 500 ,
detail = f " Failed to create or update OnePassword service account token: { str ( e ) } " ,
)
2025-09-23 10:16:48 -06:00
@base_router.get (
" /credentials/azure_credential/get " ,
response_model = AzureClientSecretCredentialResponse ,
summary = " Get Azure Client Secret Credential " ,
description = " Retrieves the current Azure Client Secret Credential for the organization. " ,
include_in_schema = False ,
)
@base_router.get (
" /credentials/azure_credential/get/ " ,
response_model = AzureClientSecretCredentialResponse ,
include_in_schema = False ,
)
async def get_azure_client_secret_credential (
current_org : Organization = Depends ( org_auth_service . get_current_org ) ,
) - > AzureClientSecretCredentialResponse :
"""
Get the current Azure Client Secret Credential for the organization .
"""
try :
auth_token = await app . DATABASE . get_valid_org_auth_token (
organization_id = current_org . organization_id ,
2025-09-26 16:35:47 -07:00
token_type = OrganizationAuthTokenType . azure_client_secret_credential . value ,
2025-09-23 10:16:48 -06:00
)
if not auth_token :
raise HTTPException (
status_code = 404 ,
detail = " No Azure Client Secret Credential found for this organization " ,
)
return AzureClientSecretCredentialResponse ( token = auth_token )
except HTTPException :
raise
except Exception as e :
LOG . error (
" Failed to get Azure Client Secret Credential " ,
organization_id = current_org . organization_id ,
error = str ( e ) ,
exc_info = True ,
)
raise HTTPException (
status_code = 500 ,
detail = f " Failed to get Azure Client Secret Credential: { str ( e ) } " ,
)
@base_router.post (
" /credentials/azure_credential/create " ,
response_model = AzureClientSecretCredentialResponse ,
summary = " Create or update Azure Client Secret Credential " ,
description = " Creates or updates a Azure Client Secret Credential for the current organization. Only one valid record is allowed per organization. " ,
include_in_schema = False ,
)
@base_router.post (
" /credentials/azure_credential/create/ " ,
response_model = AzureClientSecretCredentialResponse ,
include_in_schema = False ,
)
async def update_azure_client_secret_credential (
request : CreateAzureClientSecretCredentialRequest ,
current_org : Organization = Depends ( org_auth_service . get_current_org ) ,
) - > AzureClientSecretCredentialResponse :
"""
Create or update an Azure Client Secret Credential for the current organization .
This endpoint ensures only one valid Azure Client Secret Credential exists per organization .
If a valid token already exists , it will be invalidated before creating the new one .
"""
try :
# Invalidate any existing valid Azure Client Secret Credential for this organization
await app . DATABASE . invalidate_org_auth_tokens (
organization_id = current_org . organization_id ,
token_type = OrganizationAuthTokenType . azure_client_secret_credential ,
)
# Create the new Azure token
auth_token = await app . DATABASE . create_org_auth_token (
organization_id = current_org . organization_id ,
token_type = OrganizationAuthTokenType . azure_client_secret_credential ,
token = request . credential ,
)
LOG . info (
" Created or updated Azure Client Secret Credential " ,
organization_id = current_org . organization_id ,
token_id = auth_token . id ,
)
return AzureClientSecretCredentialResponse ( token = auth_token )
except Exception as e :
LOG . error (
" Failed to create or update Azure Client Secret Credential " ,
organization_id = current_org . organization_id ,
error = str ( e ) ,
exc_info = True ,
)
raise HTTPException (
status_code = 500 ,
detail = f " Failed to create or update Azure Client Secret Credential: { str ( e ) } " ,
)