Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 days ago.
Improve this question
I need to connect to Snowflake from Azure Databricks using the connector.
The connector requires that users put their username and password in the connections details (or in a secret store), however these are the credentials for a Snowflake user and the Snowflake account is configured to use SSO with Azure as the IdP.
Attempt 1: SAML
This attempt refers to SAML authentication for SSO in Snowflake with AAD.
Looking through the documentation for the Snowflake JDBC connector to get the connector working when using SSO you need to configure:
.option("authenticator" , "externalbrowser")
When configuring the connector in this way I get indefinite hanging of the Spark job:
snowflake_table = (spark.read
.format("snowflake")
.option("dbtable", "blah")
.option("sfURL", "xxxxxxxx.snowflakecomputing.com/")
.option("sfUser", "***") #***** value
.option("sfPassword", "***") #***** value
.option("authenticator" , "externalbrowser")
.option("sfDatabase", "blah")
.option("sfSchema", "blah")
.option("sfWarehouse", "blah")
.load()
)
With a message in the driver logs:
Initiating login request with your identity provider. A browser window should have opened for you to complete the login. If you can't see it, check existing browser windows, or your OS settings. Press CTRL+C to abort and try again...
The browser window for logging in never actually surfaces.
I found a similar comment from 2020 describing the problem: https://stackoverflow.com/questions/64330577/connection-issue-databricks-snowflake#comment113795080_64334768
I can see this isn't going to work without some proxying magic of the localhost session on the driver that serves the authentication redirect.
Attempt 2: OAUTH
Since then, I tried to go down the external OAUTH route, again using AAD. One can configure an Application Registration in Azure AD and create the integration in Snowflake. The issue then becomes how does an end user get an OAUTH2 Access Token within a Databricks notebook session, since they will need to authenticate with the registration from within the running Databricks driver session.
Aside: Somehow Azure Databricks manages to create a valid OAUTH2 token for ADLS Gen1 and Gen2 with "aud": "https://storage.azure.com" for users when credential passthrough is enabled however I have no idea what the mechanism they use to do this is. I would love to read some technical documentation on how this mechanism works under the hood, how the refresh and access tokens are generated and where they are stored.
Looking at the list of authentication flows for OAUTH in msal, the only one that doesn't require a redirect flow (e.g. Authorization code) or credentials (e.g. Client credentials, ROPC) is Device code.
This involves running the following code in Databricks (the actual implementation can be abstracted away in a shared library) and authenticating in a different browser window / tab:
import msal
import logging
import json
import sys
config = {
"client_id": "0fedeef6-71c3-42e4-ba4e-d6e2b443bd17",
"authority": "https://login.microsoftonline.com/9c5da1da-3b7d-4eb6-a0db-b83ada116551",
"scope": ["api://5b427fec-4148-4dcb-b488-9006ef357fda/session:scope:analyst"]
}
app = msal.PublicClientApplication(
config["client_id"], authority=config["authority"],
)
result = None
accounts = app.get_accounts()
if accounts:
logging.info("Account(s) exists in cache, probably with token too. Let's try.")
print("Pick the account you want to use to proceed:")
for a in accounts:
print(a["username"])
chosen = accounts[0]
result = app.acquire_token_silent(config["scope"], account=chosen)
if not result:
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
flow = app.initiate_device_flow(scopes=config["scope"])
if "user_code" not in flow:
raise ValueError(
"Fail to create device flow. Err: %s" % json.dumps(flow, indent=4))
print(flow["message"])
sys.stdout.flush()
result = app.acquire_token_by_device_flow(flow)
which presents the following message in the notebook cell:
Image of the notebook cell
A user can then present this OAUTH token in the JDBC connection details:
snowflake_table1 = (spark.read
.format("snowflake")
.option("dbtable", "CALL_CENTER")
.option("sfURL", "xxxxxxxxxx.snowflakecomputing.com/")
.option("sfUser", "xxxxxxxxxxxxx")
.option("sftoken", result["access_token"])
.option("sfRole", "analyst")
.option("sfAuthenticator" , "oauth",)
.option("sfDatabase", "SNOWFLAKE_SAMPLE_DATA")
.option("sfSchema", "TPCDS_SF100TCL")
.option("sfWarehouse", "COMPUTE_WH")
.load()
)
This approach is complex for end users and involves a convoluted authentication flow. Furthermore, if conditional access policies were introduced this method would no longer work:
Authentication flow image
Conclusion
This is the closest I've come to authenticating to a Snowflake instance that uses AAD for authentication, however it still feels very far from SSO. Is this the only way to get OAUTH tokens to an application in the same tenancy as Databricks for the user logged into Databricks? How does credential passthrough achieve this? Is there anything else I can try?
Related
My Azure Data Factory has private endpoint connection to CosmosDB and authenticates using System Assigned Managed Identity. The goal is to delete document from CosmosDB using https://learn.microsoft.com/en-us/rest/api/cosmos-db/delete-a-document called from web activity.
I created web activity in Data Factory and put the required headers following those documents
https://learn.microsoft.com/en-us/rest/api/cosmos-db/common-cosmosdb-rest-request-headers
https://learn.microsoft.com/en-us/rest/api/cosmos-db/access-control-on-cosmosdb-resources?redirectedfrom=MSDN
DELETE web activity:
I am using Azure Cosmos DB RBAC so my authorization header looks like this:
type=aad&ver=1.0&sig=token-from-oauth
To get a token I was following this post
https://medium.com/analytics-vidhya/azure-data-factory-retrieve-token-from-azure-ad-using-oauth-2-0-9a3ed3f55013
but I don't know where can I get the client_secret. I found my ADF in AAD under enterprise application so I guess client_id is application Id but there is no client secret to be found there.
get token web activity:
First obvious question is where can I find this client_secret?
The second one is why is this token needed at all? Why can't it just use managed identity for authenticating the request?
Where can I find this client_secret?
Go to azure active directory -> Inside mange go to app registration(if you not created a app registration create it ) -> Go to registered app -> Certificate & Secretes.
Why is this token needed at all? why can't it just use managed identity for authenticating the request?
Managed identities are a way to simplify the authentication process for applications running in Azure, but they do not apply to all scenarios when calling external APIs that require authentication. In these cases, you need to obtain an access token from Azure AD using a client secret.
I reproduce same thing in my environment. Follow below approach.
URL:https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/token
Scope : https://cosmos.azure.com/.default
Body: grant_type=client_credentials&client_id=<client_id>&client_secret=<client_secret>&scope=scope : https://cosmos.azure.com/.default
After execution of web1 activity you will get like this bearer token:
Inside Web2 activity provide valid URL as per below syntax:
https://{databaseaccount}.documents.azure.com/dbs/{db-id}/colls/{coll-id}/docs/{doc-id}
Add dynamic content at header part as shown in the image:
Authorization: Bearer #{activity('Web1').output.access_token}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 10 months ago.
Improve this question
In my case I need to receive and save the access token specified for tenantId inside my MS Teams bot (message extension) in order to get access to Graph API in further. There are a lot of information about adding the on-behalf-of-user authentication flow to the bot (https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/add-authentication?tabs=dotnet%2Cdotnet-sample). In this case we need to register the additional app (identity provider) on Azure portal and connect it to the bot (OAuth Connection Settings)...
But, for my case, I need to implement the client credentials authenticaion flow and receive an access token using credentials (AppId and secret) of the bot app, registered on Azure portal. In order to achive this goal, I can use msal4j library, for example:
public static String getAppAccessToken(String[] scopes) {
ConfidentialClientApplication cca;
try {
cca = ConfidentialClientApplication.builder(applicationId, ClientCredentialFactory.createFromSecret(applicationSecret))
.authority("https://login.microsoftonline.com/<<tenantId>>/")
.build();
} catch (MalformedURLException e) {
return null;
}
Set<String> scopeSet = Set.of(scopes);
ClientCredentialParameters clientCredentialParam = ClientCredentialParameters.builder(
scopeSet)
.build();
CompletableFuture<IAuthenticationResult> future = cca.acquireToken(clientCredentialParam);
return future.join().accessToken();
}
using this approach I receive the token which has expired after a while.
Questions:
is it possible to receive the access token (for specific tenantId) which hasn't expired, using the client credentials authenticaion flow inside MS Teams Bot?
should I use Bot Framework SDK or msal4j library for implementing the client credentials authenticaion flow?
the additional identity provider app (apart from Bot app) on Azure portal is required forthe client credentials authenticaion flow?
Q1 : Is it possible to receive the access token (for specific tenantId) which hasn't expired, using the client credentials authentication flow inside MS Teams Bot?
=>Access tokens are prone to expire after sometime due to security mechanism, since OAuth 2.0 client credentials grant flow provides access token instead of impersonating a user this is must have for security reasons. To generate new access tokens you would require refresh token which isn't available when you use client credentials flow but its available when used auth code grant.
Q2: Should I use Bot Framework SDK or msal4j library for implementing the client credentials authentication flow?
=>There shouldn't be an issue, You can generate token using bot authentication if required permissions are given to bot app.
Q3: The additional identity provider app (apart from Bot app) on Azure portal is required for the client credentials authentication flow?
=>If you have an existing app id associated with bot, you don't need to create one. I see docs doesn't mention this, but you don't require to create a new app in portal. You can just go and use your app(bot) in Azure Active Directory, and give required permission => generate token => Send token to graph API and get the result.
I have a user managed identity, for which I want to generate a token
I tried in user's context
az login
az account get-access-token --resource "<client-id of user managed identity>"
I get the error
Get Token request returned http error: 400 and server response: {"error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID '04b07795-8ddb-461a-bbee-02f9e1bf7b46' named 'Microsoft Azure CLI'. Send an interactive authorization request for this user and resource.
Regarding the above error, I have come across threads which suggest to add Azure CLI as preAuthorizedApplication. However I did not find this managed identity in App RegistrationsI did find an entry in `Enterprise Applications, but did not find how to pre-authorize Azure CLI here.
I tried to achieve the same in a non-user context
az login --service-principal -u <capp-id> -p <client-secret> --tenant <tenant-id>
az account get-access-token --resource "<client-id of user managed identity>"
This works.
Why does the command fail in user context? How can I make it work?
Managed Identities do not have app registrations, only a service principal (aka enterprise app).
The way you are trying to use them is not the way they are meant to be used in my opinion.
The second one working is actually just the feature of Azure AD that allows an application using the client credentials flow (client id + secret) to acquire a token for any app in the tenant.
The token won't have any permissions though, so it wouldn't be valid if you are doing authorization correctly.
You usually don't want to use the managed identities as token targets, only for acquiring tokens.
So if you need to protect an API, you'd need an app registration, where you can then allow Az CLI to call it.
You can also define application permissions and allow applications using client credentials flow to access the API with proper authorization.
In hybrid setup if client credentials grant type is used to get token and if that token is used to get on-prem user messages (https://graph.microsoft.com/v1.0/users('onpremuser#onpremdomain.com')/messages/) using graph api it fails by providing UnknownError.
When debugged on IIS logs error shown was "This token profile 'V1S2SAppOnly' is not applicable for the current protocol." error_category="invalid_token".
However if authorization code grant or resource owner password credential (ROPC) grant if used to obtain token , we were able to get messages of on prem user using graph API.
Have attached screenshot of token for both. How to make client credentials grant work for on-prem user messages access using graph API (in hybrid setup) ?
Update
Update i went and edited web.config of rest in Exchange server to have V1S2SAppOnly in profiles. After that previous error is gone and new error is seen.
Bearer+client_id="00000002-0000-0ff1-ce00-000000000000",+trusted_issuers="00000001-0000-0000-c000-000000000000#ea6064aa-d6fc-48d3-abb8-1728e1f39e0b",+token_types="app_asserted_user_v1+service_asserted_app_v1",+error="invalid_token" 2000008;reason="The+token+should+have+valid+permissions+or+linked+account+associated+with+partner+application+'00000003-0000-0000-c000-000000000000'.";error_category="invalid_grant"
I think the problem is with the aud claim, i.e. the audience for token.
For the first token that you have shared
aud value is 00000002-0000-0000-c000-000000000000. This is the resource Id for Azure AD Graph API and not Microsoft Graph API. For Microsoft Graph API, you should be using https://graph.microsoft.com or Id 00000003-0000-0000-c000-000000000000
this token is probably the one where you used client credentials grant, as there isn't any user claim
For the second token that you have shared
aud value is https://graph.microsoft.com which is correct
this token is acquired in context of a user name anoop so I guess this is the one which is working for you.
What you want is:
Application with Client credentials => Graph API => Local Exchange.
This scenario isn't supported out-of-the-box, but you can however tell your local exchange server to accept those tokens. See this answer https://stackoverflow.com/a/56131954/639153
In a nutshell, you need to change the authentication config of your front-end exchange servers to accept client credentials from the graph api. By default only delegated credentials are supported, and these settings are not documented on the exchange side.
Warning, we tested these settings, and it's working but not supported by Microsoft
This is the blog where I've found the answer to your question. https://blog.thenetw.org/2019/05/13/using-client_credentials-with-microsoft-graph-in-hybrid-exchange-setup/
I am following an older tutorial (https://code.msdn.microsoft.com/Write-Sample-App-for-79e55502) that shows how to create a web application that connects to an Azure Active Directory tenant using ADAL. There is a class library that contains DirectoryService classes that I believe does the work in retrieving AD user properties. I am wanting to create a login method for this project for security purposes, and to be able to identify what user is logged in to the application. Currently there is not a signin method that authenticates against AD by entering a username/password, so I am a little puzzled at how the app can retrieve user properties with just the AppId,Domain,and AppSecret in the Web.config without actually having someone login with either their AD creds or redirecting to login.microsoftonline/{tenantId}.....I do not know if I am making sense, but I want to be able to add a login method so a user is forced to login so it gets the claims for that specific user but if I am already using ADAL, can I also incorporate Owin?
There are two parts to your question -
1. How is this app working today, without asking end users to explicitly sign in
This sample is using Client Credential Grant, in which you don't need end users to sign in, but use the identity of an application itself to get the auth token and use it for further operations. As you mention yourself, it just needs AppId, Domain and App Secret from web.config. You can read all about it here - Client Credentials Grant
Related code to acquire token is available in MVCDirectoryGraphSample\Helpers\MVCGraphServiceHelper.cs file
AuthenticationContext authenticationContext = new AuthenticationContext(authString);
ClientCredential clientCred = new ClientCredential(ConfigurationManager.AppSettings["AppPrincipalId"],
2. How to add a login method to force users to sign in to your web application
You can start using something like Authorization Code Grant flow, instead of client credentials. Look here for documentation on different scenarios that Azure AD supports. Your case looks like a web application calling Graph API, described here
Exact code sample to do this is available here - Code Samples