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

getClient always leading to HTTP-401 #17

Open
beegentoo opened this issue Oct 10, 2023 · 5 comments
Open

getClient always leading to HTTP-401 #17

beegentoo opened this issue Oct 10, 2023 · 5 comments

Comments

@beegentoo
Copy link

beegentoo commented Oct 10, 2023

Hi all,

to be honest: I don't know if I am missing something from the big picture, if I do something wrong or if something is broken. Currently we have a home-brewed single-sign-on solution running which works fine but is not an industrial standard. That's why we plan moving to OAuth2. I am currently in a very early prototyping phase to find my way into OAuth2 and see what could how be done.

I started a small plain JS prototype project using express-oauth-server and followed the Documentation, the examples (also the additional example at https://github.com/14gasher/oauth-example as well as the original documentation at https://node-oauthoauth2-server.readthedocs.io/en/latest/model/spec.htm

Testing is done by a quickly setup Grafana-Docker-Container which has been configured to authorize via OAuth2.

[auth.generic_oauth]
enabled = true
name = OAuth
client_id = GrafanaDemo
client_secret = some_secret
scopes = user:email,read:org
auth_url = http://dev-vm:3001/login/oauth/authorize
token_url = http://dev-vm:3001/login/oauth/access_token
api_url = http://dev-vm:3001/user

Now when logging in my small prototype gets invoked. Specifically the model-function getClient(). That function is implemented with no real logic:

getClient : (clientId, clientSecret) => {
    console.log(`getClient(${clientId}, ${clientSecret})`);
    return new Promise((resolve, reject) => {
        let client = {
            id: clientId,
            clientId: clientId,
            clientSecret: clientSecret,
            grants: [
                "authorization_code",
                "refresh_token"
            ],
            redirectUris: [
                "http://dev-vm:3000/login/generic_oauth" // Grafana redirect
            ]
        };
        resolve(client);
    });
}

However, this renders an empty, dead page in the browser. Examining the call by using curl I get a HTTP-401 and no further redirect etc:

*   Trying 127.0.1.1:3001...
* Connected to dev-vm (127.0.1.1) port 3001 (#0)
> GET /login/oauth/authorize?client_id=GrafanaDemo&redirect_uri=http%3A%2F%2Fdev-vm%3A3000%2Flogin%2Fgeneric_oauth&response_type=code&scope=user%3Aemail+read%3Aorg&state=zUFDlXkJUTCSaeqipcRm9HlviQ_iZ9075WjlAIFZ2ws%3D HTTP/1.1
> Host: dev-vm:3001
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< X-Powered-By: Express
< www-authenticate: Bearer realm="Service"
< Date: Tue, 10 Oct 2023 08:24:14 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Content-Length: 0
< 
* Connection #0 to host dev-vm left intact

What am I doing wrong or am I missing something?

Additional Iinfo:
I implemented

  • getClient()
  • saveAuthorizationCode()
  • getAccessToken()
  • getAuthorizationCode()
  • generateAuthorizationCode()
  • verifyScope()
  • getUser()
  • getUserFromClient()
  • generateAccessToken()
    just to see if any other functions would be called (they don't). Also I debugged in @node-oauth/express-oauth-server and its dependency @node-oauth/oauth2-server to see what causes this error. I do not see anything fail inside.

Additional Info 2 (oh dear, it's been a long time since I filed a bug report):
Node: 18.17.1
Direct Dependencies:

  • @node-oauth/express-oauth-server 3.0.1
  • body-parser 1.20.2
  • express 4.18.2
@jankapunkt
Copy link
Member

Hi @beegentoo thank you for reporting. From your info I assume you implement the Authorization Code Grant workflow, right? There are multiple stages of the workflow, potentially returning a 401 response. Would you please also make another curl request that prints the full error response?

@beegentoo
Copy link
Author

beegentoo commented Oct 10, 2023

Hi @jankapunkt thanks for your reply. To be honest, I am not really implementing a specific workflow (yet).

The plan is to get "a foot into the door" and get started at all. But from what I read from the intended test-client (i.e. Grafana's docs) it should be the authorization code grant.

Regarding the full curl response: I got what I posted above, nothing more (see the "content-length: 0" header in the response). I managed to get a JSON-Response with an error when providing an empty model with no functions defined.

@binajmen
Copy link

Hi @beegentoo, did you manage to setup a working example ? I'm facing the same issue as you were.

@jankapunkt
Copy link
Member

@beegentoo can you please show your model implementation and how you use the middleware?

@beegentoo
Copy link
Author

Hello @binajmen,
Hello @jankapunkt

I stopped trying a few days afterwards with no success since other projects got more urgent.

I can not offer the full model implementation I had when I filed this bug since I did a few attempts to get up and running. But it should have been something like this (Github won't let me attach the file itself):

var bodyParser = require("body-parser");
var express = require("express");
var OAuthServer = require("@node-oauth/express-oauth-server");

var app = express();
app.oauth = new OAuthServer({
    model: {
        getClient : (clientId, clientSecret) => {
            console.log(`getClient(${clientId}, ${clientSecret})`);
            return new Promise((resolve, reject) => {
                /*
                let client = {
                    id: clientId,
                    clientId: clientId,
                    clientSecret: clientSecret,
                    grants: [
                        "authorization_code",
                        "refresh_token",
                        "client_credentials"
                    ],
                    redirectUris: [
                        "http://bjudas-dev-vm:3000/login/generic_oauth"
                    ]
                };
                */
                let client = {
                    clientId: clientId,
                    clientSecret: clientSecret
                };
                resolve(client);
            });
        },
        saveAuthorizationCode : (code, client, user) => {
            console.log(`saveAuthorizationCode`);
            return code;
        },
        getAccessToken : (accessToken) => {
            console.log(`getAccessToken`);
            return {
                accessToken : accessToken,
                accessTokenExpiresAt: new Date().toString()
            }
        },
        getAuthorizationCode : (authorizationCode) => {
            console.log(`getAuthrorizationCode`);
            return new Promise((resolve, reject) => {
                let code = {
                    authorizationCode : authorizationCode
                }
                resolve(code);
            });
        },
        generateAuthorizationCode : (client, user, scope) => {
            console.log(`generateAuthorizationCode`);
            return new Promise((resolve, reject) => {
                resolve("0815");
            })
        },
        verifyScope : (token, scope) => {
            console.log(`verifyScope`);
        },
        getUser : (username, password) => {
            console.log('getUser');
        },
        getUserFromClient : (client) => {
            console.log(`getUserFromClient`);
        },
        generateAccessToken : (client, user, scope) => {
            console.log(`generateAccessToken`);
        }
    },
    grants: ['authorization_code', 'refresh_token', "client_credentials"],
    accessTokenLifetime: 60 * 60 * 24, // 24 hours, or 1 day
    allowEmptyState: true,
    allowExtendedTokenAttributes: true,
  });

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false}));
app.use(app.oauth.authorize());

app.use(function(req, res) {
    console.log("Foo");
    res.send("Secret Area");
});
app.listen(3001);

As you can see: this is a module plastered with Debug-Logging just to find out exactly where the problem occurs and where I did something wrong / a bug exists.

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

No branches or pull requests

3 participants