Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client id and client secret not added to request body on authorization #4192

Closed
ThomHurks opened this issue Feb 5, 2018 · 12 comments · Fixed by #4213
Closed

Client id and client secret not added to request body on authorization #4192

ThomHurks opened this issue Feb 5, 2018 · 12 comments · Fixed by #4213

Comments

@ThomHurks
Copy link

When I click on "Authorize" in swagger-ui, fill in my username & password, client id & client secret, select "request body" for the method of including the client id & secret and then press "Authorize", the result is that the client id & secret are not added to the request body. As a result, my oauth endpoint gives an error that the client id is invalid.

I looked in the code a bit, and as far as I can see it checks if the method dropdown is set to "query" in which case it adds the client id & secret as query parameters and otherwise it always adds it as a header. Since "request body" is an option, it should check for that too and add it to the request body.

Q A
Bug or feature request? Bug
Which Swagger/OpenAPI version? 2
Which Swagger-UI version? 3.9.3
How did you install Swagger-UI? composer
Which browser & version? Chrome 64
Which operating system? MacOS High Sierra

Demonstration

swagger-bug

Expected Behavior

If I select "Request body" in the Authorize modal, then it should add the client id & secret to the request body.

Current Behavior

I select "request body" in the authorize modal, and instead it adds it to the header of the request, not the body.

Possible Solution

When the user selects "request body", add the client info to the request body, not the header of the request.

Context

Due to the current behaviour swagger-ui does not work with my project, since all my API calls require authentication which is now not possible with swagger-ui.

@scottohara
Copy link
Contributor

Yes, it does seem like there's a branch of code missing for passwordType == 'request-body' here:

if ( passwordType === "basic") {
headers.Authorization = "Basic " + btoa(username + ":" + password)
} else {
Object.assign(form, {username}, {password})
if ( passwordType === "query") {
if ( clientId ) {
query.client_id = clientId
}
if ( clientSecret ) {
query.client_secret = clientSecret
}
} else {
headers.Authorization = "Basic " + btoa(clientId + ":" + clientSecret)
}

The else branch at line 94 defaults to using the clientId + secret as an Authorization: Basic ... header.

Presumably it needs something like:

  if (clientId && clientSecret) {
    switch (passwordType) {
      case "query":
        Object.assign(query, {client_id: clientId}, {client_secret: clientSecret})
        break

      case "request-body":
        Object.assign(form, {client_id: clientId}, {client_secret: clientSecret})
        break

      default:
        headers.Authorization = "Basic " + btoa(clientId + ":" + clientSecret)
    }
  }

@shockey
Copy link
Contributor

shockey commented Feb 9, 2018

@scottohara I believe you're on the right track here.

As always... PRs welcome, everyone 😉

@schellingerht
Copy link

schellingerht commented Jun 19, 2019

I have the same issue. I'm using the same version as OP, but use the oAuth2 with grant type client_credentials (flow application).

I can fill the fields client_id, client_secret and scope in the popup. But only grant_type and scope are added in the FormData. The filled client_id and client_secret are not added. That's why I get the Auth Error Error

How can I fix this?

@monicatao
Copy link

I have the same issue. I'm using the same version as OP, but use the oAuth2 with grant type client_credentials (flow application).

I can fill the fields client_id, client_secret and scope in the popup. But only grant_type and scope are added in the FormData. The filled client_id and client_secret are not added. That's why I get the Auth Error Error

How can I fix this?

Hi did you figure out how to fix it? I get the same error

@monicatao
Copy link

I have one question. Is it possible to only use client_id and client_secret without username and password to authorize?

@MarlieChiller
Copy link

getting the exact same issue as @schellingerht with a FastAPI implementation (python). client_id and client_secret not being added to request body but other params are - fastapi/fastapi#779

@etlao
Copy link

etlao commented Oct 27, 2021

@schellingerht @monicatao @Charlie-iProov
client_id and client_secret are transferred in "Authorization" header. In base64 string.

I have Swashbuckle.AspNetCore - version 6.2.3. And this bug is still not fixed.

@vistpp
Copy link

vistpp commented Oct 5, 2022

6.4.0 and year after bug is still here. :)

@open-collar
Copy link

I am also having problems caused by this issue. It looks like the code in Master is correct, but it isn't working.

@open-collar
Copy link

It looks like the fault is in oauth2-authorize.js. In the authorize method there is a switch on flow and then uses the authorizeApplication method for "client_credentials" rather than authorizePassword. The authorizeApplication method does not attempt to set the client_id or client_secret fields.

@vistpp
Copy link

vistpp commented Oct 27, 2022

Yes, it sets clientId / clientSecret on Authorization header with Basic scheme. Which I don't think is a standard way to authorize via client credentials flow according to OAuth2 protocol. At least Azure AD B2C doesn't support it.

@open-collar
Copy link

I'm using Swagger UI from a .NET Core Web App, with an Azure AD app registration. My solution in the end was to inject a JavaScript file with fixes for the problems I found:

webApplication.UseSwaggerUI(c =>
{
    // ...
    c.InjectJavascript("/swagger-ui/custom.js");
    // ...
});

The JavaScript file contains this:

window.fetch = function (fetch) {
    return function () {
        const req = arguments[1];
        // Intercept the requests to get the OAuth2 token from Azure AD to ensure that 
        // CORS is not enabled.
        if (req.url.endsWith('/token')) {
            req.mode = 'no-cors';
        }
        return fetch.apply(window, arguments);
    };
}(window.fetch);

document.addEventListener('DOMContentLoaded', function load() {
    if (!window.ui) {
        return setTimeout(load, 50);
    }

    // Replace the standard implementation of authorizeApplication in actions.js with a modified version that adds the 
    // client ID and client secret.
    // Based on code taken from $\swagger-ui\src\core\plugins\auth\actions.js
    ui.authActions.authorizeApplication = function (auth) {

        const { schema, scopes, name, clientId, clientSecret } = auth;
        const headers = { Authorization: 'Basic ' + btoa(clientId + ':' + clientSecret)
        };
        const form = {
            grant_type: 'client_credentials',
            scope: scopes.join(' ')
        };
        if (clientId) {
            form.client_id = clientId;
        }
        if (clientSecret) {
            form.client_secret = clientSecret;
        }

        return ui.authActions.authorizeRequest({ body: buildFormData(form), name, url: schema.get('tokenUrl'), auth, headers });
    }
}, false);

// No changes here, just the easiest way of using it.
// Taken from $\swagger-ui\src\core\utils.js
const buildFormData = function (data) {
    let formArr = []

    for (let name in data) {
        let val = data[name]
        if (val !== undefined && val !== '') {
            formArr.push([name, '=', encodeURIComponent(val).replace(/%20/g, '+')].join(''))
        }
    }
    return formArr.join('&')
}

I don't think it is a great solution, but it has worked for my very specific use-case. I have looked at creating a PR for the Swagger UI code, but reading the various issues related to this problem, I am not sure I fully understand the intent of the code that is there and don't want to break existing functionality.

I have also had to create a proxy controller (using AspNetCore.Proxy) to deal with a CORS issue (acting as a proxy for the token endpoint).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants