Forum Discussion

RICRUS's avatar
RICRUS
New Member
3 years ago
Solved

verb-type error for OAuth connector

I am trying to build an OAuth2 connector using the standard boilerplate template with VSCode Power Query SDK.

 

When I run 'Set credential' I am presented with the login screen modal as expected. But after successfully logging into my application I get a 'verb-type' error returned. I am not sure why this is? Can anyone see any issues with my code?

 

I am running Windows 11 (ARM) - if that helps.

 

The error:

 

Unhandled Exception: System.Net.ProtocolViolationException: Cannot send a content-body with this verb-type.
   at Microsoft.Mashup.Engine1.Runtime.Extensibility.ExtensionOAuthFactory.Provider.Microsoft.Mashup.OAuth.IOAuthProvider.FinishLogin(Byte[] serializedContext, Uri callbackUri, String state)
   at PQTest.Common.Commands.<>c__DisplayClass11_0.<SetCredentalInteractive>b__0()
   at PQTest.Common.Commands.ConfigureMashupEnvironmentAndRun(Configuration configuration, Action action)
   at PQTest.Console.Program.RunCredentialCommand(CommandLineApplication app, CommandLineApplication command, Action`1 invoke, DataSourceSetting dataSourceSetting)
   at Microsoft.Extensions.CommandLineUtils.CommandLineApplication.Execute(String[] args)
   at PQTest.Console.Program.Main(String[] args)

 

 

My code:

 

// This file contains your Data Connector logic
section MyConnector;

// OuraCloud OAuth2 values
client_id = Text.FromBinary(Extension.Contents("client_id.txt"));
client_secret = Text.FromBinary(Extension.Contents("client_secret.txt"));
redirect_uri = "https://oauth.powerbi.com/views/oauthredirect.html";
token_uri = "http://mydomain.com/oauth2/token";
authorize_uri = "http://mydomain.com/v2/oauth2/authorize";
logout_uri = "https://login.microsoftonline.com/logout.srf";

// Login modal window dimensions
windowWidth = 720;
windowHeight = 1024;

// OAuth2 scope
scope_prefix = "";
scopes = {
    "daily"
};

[DataSource.Kind="MyConnector", Publish="MyConnector.Publish"]
shared MyConnector.Contents = (url as text) =>
    let
        source = Json.Document(Web.Contents(url))
    in
        source; 

// Data Source Kind description
MyConnector = [
    TestConnection = (dataSourcePath) => { "MyConnector.Contents", dataSourcePath },
    Authentication = [
        OAuth = [
            StartLogin=StartLogin,
            FinishLogin=FinishLogin,
            Refresh=Refresh,
            Logout=Logout
        ]
    ],
    Label = Extension.LoadString("DataSourceLabel")
];

// Data Source UI publishing description
MyConnector.Publish = [
    Beta = true,
    Category = "Other",
    ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") },
    LearnMoreUrl = "https://powerbi.microsoft.com/",
    SourceImage = MyConnector.Icons,
    SourceTypeImage = MyConnector.Icons
];

// Helper functions for OAuth2: StartLogin, FinishLogin, Refresh, Logout
StartLogin = (resourceUrl, state, display) =>
    let
        authorizeUrl = authorize_uri & "?" & Uri.BuildQueryString([
            response_type = "code",
            client_id = client_id,  
            redirect_uri = redirect_uri,
            state = state,
            scope = GetScopeString(scopes, scope_prefix)
        ])
    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
                    TokenMethod("authorization_code", "code", parts[code])
    in
        result;

Refresh = (resourceUrl, refresh_token) => TokenMethod("refresh_token", "refresh_token", refresh_token);

Logout = (token) => logout_uri;

TokenMethod = (grantType, tokenField, code) =>
    let
        queryString = [
            grant_type = "authorization_code",
            redirect_uri = redirect_uri,
            client_id = client_id,
            client_secret = client_secret
        ],
        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;

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;
        

MyConnector.Icons = [
    Icon16 = { Extension.Contents("MyConnector16.png"), Extension.Contents("MyConnector20.png"), Extension.Contents("MyConnector24.png"), Extension.Contents("MyConnector32.png") },
    Icon32 = { Extension.Contents("MyConnector32.png"), Extension.Contents("MyConnector40.png"), Extension.Contents("MyConnector48.png"), Extension.Contents("MyConnector64.png") }
];

 

 

 

 

 

  • The error was caused by `token_uri` and `authorize_uri` being called over HTTP and not HTTPS.

1 Reply

  • The error was caused by `token_uri` and `authorize_uri` being called over HTTP and not HTTPS.