Hi, there:
We have a service which respond to get/put request with AAD auth, we tried to pass auth and able to call the API and pull the data out into PowerBI.
We tried to build the power BI connector in Visual studio with AAD auth, after I enter user name and password, it always return the following error:
AADSTS65002: Consent between first party applications and resources must be configured via preauthorization
Check our app in AAD Onborading portal, it doesn't have any Delegated Permissions or Application Permission being set, should we go ahead and set them - if yes, which Application Permission/Delegated Permission to set?
Is there anything else we need to do to fix the AADSTS65002 preauthorization error?
Thanks!
section MyGraph;
//
// OAuth configuration settings
//
windowWidth = 720;
windowHeight = 1024;
//
// Exported function(s)
//
[DataSource.Kind="MyGraph", Publish="MyGraph.UI"]
shared MyGraph.Feed = () =>
let
source = OData.Feed("https://graph.microsoft.com/v1.0/me/", null, [ ODataVersion = 4, MoreColumns = true ])
in
source; //
// Data Source definition
//
MyGraph = [
TestConnection = (dataSourcePath) => { "MyGraph.Feed" },
Authentication = [
OAuth = [
StartLogin=StartLogin,
FinishLogin=FinishLogin,
Refresh=Refresh,
Logout=Logout
]
],
Label = "My Graph Connector"
];
//
// UI Export definition
//
MyGraph.UI = [
Beta = true,
ButtonText = { "MyGraph.Feed", "Connect to Graph" },
SourceImage = MyGraph.Icons,
SourceTypeImage = MyGraph.Icons
];
MyGraph.Icons = [
Icon16 = { Extension.Contents("MyGraph16.png"), Extension.Contents("MyGraph20.png"), Extension.Contents("MyGraph24.png"), Extension.Contents("MyGraph32.png") },
Icon32 = { Extension.Contents("MyGraph32.png"), Extension.Contents("MyGraph40.png"), Extension.Contents("MyGraph48.png"), Extension.Contents("MyGraph64.png") }
];
//
// OAuth implementation
//
// See the following links for more details on AAD/Graph OAuth:
// * https://docs.microsoft.com/en-us/azure/active-directory/active-directory-protocols-oauth-code
// * https://graph.microsoft.io/en-us/docs/authorization/app_authorization
//
// StartLogin builds a record containing the information needed for the client
// to initiate an OAuth flow. Note for the AAD flow, the display parameter is
// not used.
//
// resourceUrl: Derived from the required arguments to the data source function
// and is used when the OAuth flow requires a specific resource to
// be passed in, or the authorization URL is calculated (i.e. when
// the tenant name/ID is included in the URL). In this example, we
// are hardcoding the use of the "common" tenant, as specified by
// the 'authorize_uri' variable.
// state: Client state value we pass through to the service.
// display: Used by certain OAuth services to display information to the
// user.
//
// Returns a record containing the following fields:
// LoginUri: The full URI to use to initiate the OAuth flow dialog.
// CallbackUri: The return_uri value. The client will consider the OAuth
// flow complete when it receives a redirect to this URI. This
// generally needs to match the return_uri value that was
// registered for your application/client.
// WindowHeight: Suggested OAuth window height (in pixels).
// WindowWidth: Suggested OAuth window width (in pixels).
// Context: Optional context value that will be passed in to the FinishLogin
// function once the redirect_uri is reached.
//
StartLogin = (resourceUrl, state, display) =>
let
authorizeUrl = authorize_uri & "?" & Uri.BuildQueryString([
client_id = client_id,
redirect_uri = redirect_uri,
state = state,
scope = GetScopeString(scopes, scope_prefix),
response_type = "code",
response_mode = "query",
login = "login"
])
in
[
LoginUri = authorizeUrl,
CallbackUri = redirect_uri,
WindowHeight = 720,
WindowWidth = 1024,
Context = null
]; // FinishLogin is called when the OAuth flow reaches the specified redirect_uri.
// Note for the AAD flow, the context and state parameters are not used.
//
// context: The value of the Context field returned by StartLogin. Use this to
// pass along information derived during the StartLogin call (such as
// tenant ID)
// callbackUri: The callbackUri containing the authorization_code from the service.
// state: State information that was specified during the call to StartLogin.
FinishLogin = (context, callbackUri, state) =>
let
// parse the full callbackUri, and extract the Query string
parts = Uri.Parts(callbackUri)[Query],
// if the query string contains an "error" field, raise an error
// otherwise call TokenMethod to exchange our code for an access_token
result = if (Record.HasFields(parts, {"error", "error_description"})) then
error Error.Record(parts[error], parts[error_description], parts)
else
TokenMethod("authorization_code", "code", parts[code])
in
result;
// Called when the access_token has expired, and a refresh_token is available.
//
Refresh = (resourceUrl, refresh_token) => TokenMethod("refresh_token", "refresh_token", refresh_token);
Logout = (token) => logout_uri;
// grantType: Maps to the "grant_type" query parameter.
// tokenField: The name of the query parameter to pass in the code.
// code: Is the actual code (authorization_code or refresh_token) to send to the service.
TokenMethod = (grantType, tokenField, code) =>
let
queryString = [
client_id = client_id,
scope = GetScopeString(scopes, scope_prefix),
grant_type = grantType,
redirect_uri = redirect_uri
],
queryWithCode = Record.AddField(queryString, tokenField, code),
tokenResponse = Web.Contents(token_uri, [
Content = Text.ToBinary(Uri.BuildQueryString(queryWithCode)),
Headers = [
#"Content-type" = "application/x-www-form-urlencoded",
#"Accept" = "application/json"
],
ManualStatusHandling = {400}
]),
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;
//
// Helper Functions
//
Value.IfNull = (a, b) => if a <> null then a else b;
GetScopeString = (scopes as list, optional scopePrefix as text) as text =>
let
prefix = Value.IfNull(scopePrefix, ""),
addPrefix = List.Transform(scopes, each prefix & _),
asText = Text.Combine(addPrefix, " ")
in
asText;