The ultimate Fabric, Power BI, SQL, and AI community-led learning event. Save €200 with code FABCOMM.
Get registeredCompete to become Power BI Data Viz World Champion! First round ends August 18th. Get started.
I am working on a custom Power BI connector and have defined authParams as optional authentication parameters, which include ClientID, ClientSecret, TenantID, RedirectURL, and Scope. These serve as custom input fields where users provide authentication details.
I want to pass the user input from authParams to my StartLogin and FinishLogin functions for authentication. However, I am unsure how to correctly access and use these values within the authentication flow.
Here is my sample code:
section Custom.PBI.Connector;
[DataSource.Kind = "Custom.PBI.Connector", Publish = "Custom.PBI.Connector.Publish"]
shared Custom.PBI.Connector.DataSource = Value.ReplaceType(ConnectionImpl, ConnectionType);
// Define Connection Implementation
ConnectionImpl = (dsn as text, optional advancedOptions as text, optional options as record, optional authParams as record) =>
let
Credential = Extension.CurrentCredential(),
AuthKind = Credential[AuthenticationKind]?,
CredentialConnectionString =
if AuthKind = "UsernamePassword" then
[
UID = Credential[Username],
PWD = Credential[Password]
]
else
[],
ConnectionStringPartial = [
DSN = dsn
] & CredentialConnectionString,
ConnectionString = RecordToString(ConnectionStringPartial)
& ";"
& (if advancedOptions <> null then advancedOptions else ""),
OAuthOptions =
if AuthKind = "OAuth" then
let
AccessToken = Credential[access_token]?
in
if AccessToken = null then
error "Access Token is missing. Please authenticate again."
else
[CredentialConnectionString = RecordToString([AccessToken = AccessToken])]
else
[],
finalDatasource =
try
if HasQuery(options) then
Odbc.Query(ConnectionString, options[Query])
else
Odbc.DataSource(ConnectionString, OAuthOptions)
otherwise
error "Failed to connect. Please check your credentials and network settings.",
PersistedResult = if finalDatasource <> null then finalDatasource else error "No data returned."
in
PersistedResult;
// Define ConnectionType
ConnectionType = type function (
dsn as text,
optional advancedOptions as text,
optional options as record,
optional authParams as record
) as table;
// Define OAuth Login Handling
StartLogin = (resourceUrl, state, display) =>
let
authUrl = "https://login.provider.com/" & tenant_id & "/oauth2/authorize?",
queryString = Uri.BuildQueryString([
client_id = client_id,
redirect_uri = redirect_uri,
response_type = "code",
scope = scope,
state = state
]),
AuthorizeUrl = authUrl & queryString
in
[
LoginUri = AuthorizeUrl,
CallbackUri = redirect_uri,
WindowHeight = 720,
WindowWidth = 1024,
Context = null
];
FinishLogin = (context, callbackUri, state) =>
let
parts = Uri.Parts(callbackUri)[Query],
result =
if Record.HasFields(parts, {"error", "error_description"}) then
error Error.Record(parts[error], parts[error_description], parts)
else
ExchangeAuthorizationCode(parts[code])
in
result;
ExchangeAuthorizationCode = (authCode as text) as record =>
let
tokenUrl = "https://login.provider.com/" & tenant_id & "/oauth2/token",
queryParams = [
grant_type = "authorization_code",
code = authCode,
redirect_uri = redirect_uri,
client_id = client_id,
client_secret = client_secret
],
tokenResponse = Web.Contents(tokenUrl, [
Content = Text.ToBinary(Uri.BuildQueryString(queryParams)),
Headers = ["Content-Type" = "application/x-www-form-urlencoded", "Accept" = "application/json"]
]),
body = Json.Document(tokenResponse),
result =
if Record.HasFields(body, {"error", "error_description"}) then
error Error.Record(body[error], body[error_description], body)
else
body
in
result;
// Publish Data Source UI Details
Custom.PBI.Connector.Publish = [
Beta = false,
Category = "Custom",
ButtonText = {"Connect", "Connect to a custom data source"},
LearnMoreUrl = "https://yourdocumentationlink.com",
SupportsDirectQuery = true
];
How can I retrieve these user-provided values in my StartLogin and FinishLogin functions and use them dynamically in the authentication process? Any guidance or examples would be greatly appreciated.
Thanks in advance!
Solved! Go to Solution.
This didn't help to solve my problem. but I have made the below changes that resolved my issue.
MyCustomConnector = [
TestConnection = (dataSource) =>
let
jsonDoc = Json.Document(dataSource),
DSN = Record.FieldOrDefault(jsonDoc, "dsn", ""),
AuthURL = Record.FieldOrDefault(jsonDoc, "auth_url", null),
TokenURL = Record.FieldOrDefault(jsonDoc, "token_url", null),
ClientID = Record.FieldOrDefault(jsonDoc, "client_id", null),
ClientSecret = Record.FieldOrDefault(jsonDoc, "client_secret", null),
RedirectURL = Record.FieldOrDefault(jsonDoc, "redirect_url", null),
Scope = Record.FieldOrDefault(jsonDoc, "scope", null),
hasOAuth = (AuthURL <> null and TokenURL <> null and ClientID <> null and ClientSecret <> null and RedirectURL <> null and Scope <> null),
ConnectionDetails = if hasOAuth then
{
"Custom.DataSource",
DSN,
AuthURL,
TokenURL,
ClientID,
ClientSecret,
RedirectURL,
Scope
}
else
{
"Custom.DataSource",
DSN
}
in
ConnectionDetails,
Authentication = [
UsernamePassword = [],
Windows = [SupportsAlternateCredentials = true],
Implicit = [],
OAuth = [
StartLogin = StartLogin,
FinishLogin = FinishLogin,
Refresh = RefreshToken,
Label = "Organizational Account"
]
],
Label = Extension.LoadString("ConnectorDisplayName")
];
Hi @asingh1-tibco,
We wanted to kindly check in to see if everything is working as expected after trying the suggested solution. If there’s anything else we can assist with, please don’t hesitate to ask.
If the issue is resolved, we’d appreciate it if you could mark the helpful reply as Accepted Solution — it helps others who might face a similar issue.
Warm regards,
Prasanna Kumar
Hi @asingh1-tibco,
As we haven’t heard back from you, we wanted to kindly follow up to check if the solution provided for the issue worked? or Let us know if you need any further assistance?
If our response addressed, please mark it as Accept as solution and click Yes if you found it helpful.
Regards,
Prasanna Kumar
Thank you for reaching out on the Microsoft Fabric Community Forum.
[DataSource.UI.Prompts]
Custom.PBI.Connector = [
CredentialPrompt = [
AuthenticationKind = "OAuth",
Label = "Enter your OAuth Details",
Fields = [
client_id = [Label = "Client ID", Type = "Text"],
client_secret = [Label = "Client Secret", Type = "Text", IsPassword = true],
tenant_id = [Label = "Tenant ID", Type = "Text"],
redirect_uri = [Label = "Redirect URI", Type = "Text"],
scope = [Label = "Scope", Type = "Text"]
]
]
];
Define OAuth Authentication Kind
In your DataSource.Kind metadata
[DataSource.Kind = "Custom.PBI.Connector"]
shared Custom.PBI.Connector = ... // your data source
Custom.PBI.Connector = [
Authentication = [
OAuth = [
StartLogin = StartLogin,
FinishLogin = FinishLogin,
Label = Extension.LoadString("OAuthLabel")
]
]
];
3. Update StartLogin
StartLogin = (resourceUrl, state, display) =>
let
creds = Extension.CurrentCredential(),
client_id = creds[client_id],
tenant_id = creds[tenant_id],
redirect_uri = creds[redirect_uri],
scope = creds[scope],
authUrl = "https://login.provider.com/" & tenant_id & "/oauth2/authorize?",
queryString = Uri.BuildQueryString([
client_id = client_id,
redirect_uri = redirect_uri,
response_type = "code",
scope = scope,
state = state
])
in
[
LoginUri = authUrl & queryString,
CallbackUri = redirect_uri,
WindowHeight = 720,
WindowWidth = 1024,
Context = null
];
Update FinishLogin and ExchangeAuthorizationCode
FinishLogin = (context, callbackUri, state) =>
let
creds = Extension.CurrentCredential(),
parts = Uri.Parts(callbackUri)[Query],
result =
if Record.HasFields(parts, {"error", "error_description"}) then
error Error.Record(parts[error], parts[error_description], parts)
else
ExchangeAuthorizationCode(parts[code], creds)
in
result;
ExchangeAuthorizationCode = (authCode as text, creds as record) as record =>
let
tokenUrl = "https://login.provider.com/" & creds[tenant_id] & "/oauth2/token",
queryParams = [
grant_type = "authorization_code",
code = authCode,
redirect_uri = creds[redirect_uri],
client_id = creds[client_id],
client_secret = creds[client_secret]
],
tokenResponse = Web.Contents(tokenUrl, [
Content = Text.ToBinary(Uri.BuildQueryString(queryParams)),
Headers = [
"Content-Type" = "application/x-www-form-urlencoded",
"Accept" = "application/json"
]
]),
body = Json.Document(tokenResponse),
result =
if Record.HasFields(body, {"error", "error_description"}) then
error Error.Record(body[error], body[error_description], body)
else
body
in
result;
If this response is helpful, please consider marking it as the accepted solution and giving it a thumbs-up to help others in the community.
Thank you!
This didn't help to solve my problem. but I have made the below changes that resolved my issue.
MyCustomConnector = [
TestConnection = (dataSource) =>
let
jsonDoc = Json.Document(dataSource),
DSN = Record.FieldOrDefault(jsonDoc, "dsn", ""),
AuthURL = Record.FieldOrDefault(jsonDoc, "auth_url", null),
TokenURL = Record.FieldOrDefault(jsonDoc, "token_url", null),
ClientID = Record.FieldOrDefault(jsonDoc, "client_id", null),
ClientSecret = Record.FieldOrDefault(jsonDoc, "client_secret", null),
RedirectURL = Record.FieldOrDefault(jsonDoc, "redirect_url", null),
Scope = Record.FieldOrDefault(jsonDoc, "scope", null),
hasOAuth = (AuthURL <> null and TokenURL <> null and ClientID <> null and ClientSecret <> null and RedirectURL <> null and Scope <> null),
ConnectionDetails = if hasOAuth then
{
"Custom.DataSource",
DSN,
AuthURL,
TokenURL,
ClientID,
ClientSecret,
RedirectURL,
Scope
}
else
{
"Custom.DataSource",
DSN
}
in
ConnectionDetails,
Authentication = [
UsernamePassword = [],
Windows = [SupportsAlternateCredentials = true],
Implicit = [],
OAuth = [
StartLogin = StartLogin,
FinishLogin = FinishLogin,
Refresh = RefreshToken,
Label = "Organizational Account"
]
],
Label = Extension.LoadString("ConnectorDisplayName")
];
Hi @asingh1-tibco,
Thanks for reaching out to MS Fabric community support
Power BI Custom Connectors do not directly support passing user-defined authentication parameters (authParameters) into StartLogin and FinishLogin at runtime. But you can work around this limitation using Static Parameters and Custom Authentication Records.
Thanks,
Prashanth Are
MS Fabric community support
If this post helps, then please consider Accept it as the solution to help the other members find it more quickly and give Kudos if helped you resolve your query
Thanks for the response. If possible can you please guide with sample code to solve this issue.