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

Feature: provide getting auth token and user profile api for client-server used. #47

Merged
merged 12 commits into from
Sep 22, 2022

Conversation

kokokuo
Copy link
Contributor

@kokokuo kokokuo commented Sep 5, 2022

Description

Provide an getting token info POST API /auth/token and get user profile GET API /auth/user-profile for client-server to authenticate and get user information. Otherwise, we also support providing auth credentials data ( e.g: token ) from query string or payload. So it not only has one way to provide by Authorization in the header.

We will talk about it in two sections.

How to use /auth/token and /auth/user-profile API

The /auth/token POST API is used to get tokens by passing user identity information, e.g: username or password. After you get the token, you could continue to get the current user's basic information by GET /auth/user-profile with the above got token.

Also if you would like to query the data endpoint API, you also need the token.

Set auth options to make API work first.

The type value should provide satisfy the key name under auth options, e.g: basic, simple-token, password-file.. and so on.

Sample 1

auth:
 options:
  basic:
   htpasswd-file: 
     ....

Then you could access auth and get a token with API /auth/token endpoint.

{
  "type": "basic"
  // below is the auth type needed for other payload data, e.g: username, password
}

Sample 2

If you also added password-file auth type options in settings:

auth:
 options:
  basic:
   htpasswd-file: 
    path: './yyyy'
  simple-token:
   - name: user1
     token: '....'

Then the auth API /auth/token with type could use simple-token and basic.

Error message

If you do not provide type query string, it will show 400 status code with the message, like below:

{
  "message": "auth type 'xxx',in options not supported, authenticator only supported"
}

Other payloads like username, password

Current built-in authenticators: simple-token, password-file and basic both need to be given username and password fields in the payload.

{
 "type": "basic"
 "username": "user1",
 "passowrd": "test1"
}

If the client missed some field e.g: username or password it will throw an error message with status 400:

{
  "message": "error message....."
}

After you get the token e.g: dXNlcjE6dGVzdDE=, then you could get the user profile following the above /auth/user-profile GET API we talked.

Just call the GET API /auth/user-profile and set the Authorization with token value e.g: dXNlcjE6dGVzdDE= in request header.

Customize your Authenticator

If the above built-in basic, password-files, simple-token authenticator could not satisfy, we could define the authenticator, you should inherit BaseAuthenticator and implement the getTokenInfo and authCredential method.

Support providing token or other related auth credentials data from query string or payload

We also define another middleware called authSourceNormalizerMiddleware to support clients who could pass the auth credentials data query string or payload.

Set the auth-source options

Default, the client could through auth key and base64 with JSON stringify flow to encode your Authroizartion key with value. like below:

/auth/user-profile?auth=Base64(JSON.stringfiy({'Authorization': `Basic dXNlcjE6dGVzdDE=`})) 

Then we will find the auth in the query string and check if the format is Base64 or not, if yes, we will try to decode it and move it to the header, but, currently, we only accept the Authorization key name in the object. ( We will provide a whitelist feature to make user could define the acceptable key name in the object )

If you would like to change the key name like x-auth to replace the auth key, or you would like to set x-auth by payload, the you could define it in auth-source options:

auth-source:
  key:'x-auth'  # default is "auth" key name
  in: 'query'    # default is "query",  and we support "payload" or "query" two types

like above, then when you send a query to the data endpoint e.g: /orders, it looks like below:

/orders?x-auth=Base64(JSON.stringfiy({'Authorization': `Basic dXNlcjE6dGVzdDE=`})) 

Commit Message

  • 04eb9f4 feat(serve): provide auth identity route for client server used.
  • f13dc42 fix(serve): add error handler for an auth route middleware
  • 016656a fix(serve): move server.close to after each function in test cases
  • 1a0930a fix(serve): move the auth credential middleware checking option logistic to onActivate method
  • 044b649 fix(serve): add checking enable condition and option value in onActivate method of auth route middleware
  • 7c3f9b4 fix(serve): the /auth route still need to contain Authorization header issue
  • 4dfd5cc fix(serve): remove checking user credential logistic and only generate token
  • 7f6d397 fix(serve): change get method of /auth/token endpoint to post method
  • 14f3bb9 feat(serve): add /auth/user-profile api to get user profile.
  • ac2d8d5 feat(serve): add auth source normalizer middleware to support query string source auth data
  • 258157f fix(serve): import 'koa-bodyparser' in the each authenticator to make jest run correct.
  • 2bce5e8 fix(serve,labs): refactor error message when not provide auth types in options, and set disable for auth in vulcan.yaml template in labs project

@codecov-commenter
Copy link

codecov-commenter commented Sep 5, 2022

Codecov Report

Base: 92.52% // Head: 92.77% // Increases project coverage by +0.24% 🎉

Coverage data is based on head (2bce5e8) compared to base (06aca46).
Patch coverage: 94.87% of modified lines in pull request are covered.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop      #47      +/-   ##
===========================================
+ Coverage    92.52%   92.77%   +0.24%     
===========================================
  Files          224      229       +5     
  Lines         3117     3223     +106     
  Branches       370      391      +21     
===========================================
+ Hits          2884     2990     +106     
+ Misses         169      168       -1     
- Partials        64       65       +1     
Flag Coverage Δ
build 94.87% <ø> (ø)
cli 91.42% <ø> (ø)
core 93.22% <ø> (ø)
extension-dbt 97.43% <ø> (ø)
extension-debug-tools 98.11% <ø> (ø)
extension-driver-duckdb 100.00% <ø> (ø)
integration-testing 96.15% <100.00%> (+1.15%) ⬆️
serve 90.12% <94.77%> (+1.31%) ⬆️
test-utility ∅ <ø> (∅)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...kages/serve/src/models/extensions/authenticator.ts 90.90% <ø> (ø)
...es/serve/src/lib/auth/passwordFileAuthenticator.ts 81.81% <81.81%> (+0.73%) ⬆️
...c/lib/middleware/auth/authCredentialsMiddleware.ts 85.00% <85.00%> (ø)
.../middleware/auth/authSourceNormalizerMiddleware.ts 90.00% <90.00%> (ø)
.../integration-testing/src/example1/projectConfig.ts 100.00% <100.00%> (ø)
packages/serve/src/lib/app.ts 84.84% <100.00%> (+0.97%) ⬆️
...kages/serve/src/lib/auth/httpBasicAuthenticator.ts 87.23% <100.00%> (+2.23%) ⬆️
...ges/serve/src/lib/auth/simpleTokenAuthenticator.ts 100.00% <100.00%> (ø)
...es/serve/src/lib/middleware/auth/authMiddleware.ts 100.00% <100.00%> (ø)
...ve/src/lib/middleware/auth/authRouterMiddleware.ts 100.00% <100.00%> (ø)
... and 7 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@kokokuo kokokuo marked this pull request as ready for review September 5, 2022 08:20
@kokokuo kokokuo requested a review from oscar60310 September 5, 2022 08:20
@kokokuo kokokuo changed the title Feature: provide auth identity route for client-server used. [WIP] Feature: provide auth identity route for client-server used. Sep 14, 2022
@kokokuo kokokuo removed the request for review from oscar60310 September 14, 2022 06:38
Copy link
Contributor

@oscar60310 oscar60310 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other part LGTM.

Comment on lines 90 to 101
public async authIdentity(ctx: KoaContext) {
const username = ctx.request.query['username'] as string;
const password = ctx.request.query['password'] as string;
if (!username || !password)
throw new Error('please provide "username" and "password".');

if (
!(username in this.usersCredentials) ||
!(md5(password) === this.usersCredentials[username].md5Password)
)
throw new Error('authenticate user identity failed.');

const token = Buffer.from(`${username}:${password}`).toString('base64');

return {
token: token,
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function ( and the /auth endpoint) only provide the information of authenticators, e.g. token, redirect URL ...etc. But we don't need to do auth check here, it is the job of authCredential function, we don't need to duplicate the logic here. So do other authenticators.

  public async authIdentity(ctx: KoaContext) {
    const username = ctx.request.query['username'] as string;
    const password = ctx.request.query['password'] as string;
    if (!username || !password)
      throw new Error('please provide "username" and "password".');
    // encode it anyway, let users fail at authCredential function
    const token = Buffer.from(`${username}:${password}`).toString('base64');

    return {
      token: token,
    };
  }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @oscar60310 the for suggestion, after discussion I have made it only generate the token, and provide another API /auth/user-profile to get the user data with the token which got from /auth/token

Comment on lines 53 to 56
if (isEmpty(options))
throw new Error(
'please set at least one auth type and user credential when you enable the "auth" options.'
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Can we check the config in onActivate function?

Copy link
Contributor Author

@kokokuo kokokuo Sep 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for @oscar60310 suggestion. it has been fixed.

Comment on lines 66 to 69
if (isEmpty(this.options))
throw new Error(
'please set at least one auth type and user credential when you enable the "auth" options.'
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we check the option at onActivate function too?

if (this.enable && isEmpty(xxxxx

Copy link
Contributor Author

@kokokuo kokokuo Sep 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for @oscar60310 suggestion. it has been fixed.

if (authenticator.activate) await authenticator.activate();
}
// setup route
this.setAuthRoute();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we only mount our routes only when extension is enabled?

if(this.enable) this.setAuthRoute();

Copy link
Contributor Author

@kokokuo kokokuo Sep 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for @oscar60310 suggestion, it has been fixed.

}

private setAuthRoute() {
this.router.get(`/auth`, async (context: KoaContext, next) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer using POST instead of GET to send our credentials.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ought this route to be public? For now, it's protected by authCredentialMiddleware, that is, we have to send credentials before knowing how to get one.


curl --location --request GET 'http://localhost:3000/auth?type=basic&username=ivan&password=123'


curl --location --request GET 'http://localhost:3000/auth?type=basic&username=ivan&password=123'
--header 'Authorization: Basic aXZhbjoxMjM='

Copy link
Contributor Author

@kokokuo kokokuo Sep 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for @oscar60310 suggestion and finding the issue. The API has changed to POST and rename it to /auth/token. Otherwise, the original calling the /auth also needs the --header 'Authorization: Basic aXZhbjoxMjM=' issue also has been fixed, thanks a lot!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also add more test cases related to authentication in the integration-testing package and change the structure.

// Assert
expect(response.statusCode).toEqual(400);
expect(response.body).toEqual(expected);
server.close();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should close server in afterEach hooks, or they become dangling if test cases failed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for @oscar60310 suggestion. it has been fixed.

@kokokuo kokokuo changed the title [WIP] Feature: provide auth identity route for client-server used. Feature: provide auth identity route for client-server used. Sep 21, 2022
- add "AuthRouteMiddleware" for creating each route by indicated auth options.
- add "authIdentity" for authenticate user identity in authenticator.
- add test cases for auth identity and "AuthRouteMiddleware".
- add disable options of "auth" in influenced test cases.
…der issue

- add the /auth route checking in auth credential middleware.
- refactor integration-testing example structure and add authenticate example
…e token

- change "/auth" to "/auth/token" and rename "authIdentity" method to "getTokenInfo".
- remove checking user credential logistic in "getTokenInfo" method.
- update all related test cases
- Update the get method of "/auth/token" to post method.
- Update getting request fields source in "basic", "password-files" and "token" in "getTokenInfo" method of authenticator.
- Update related test cases.
…tring source auth data

- add "authSourceNormalizerMiddleware" and its options to support client providing auth data from query string or payload.
- rename "authRouteMiddleware" to "authRouterMiddleware".
- create "BaseAuthMiddleware" to collect same logistic part for activate method in auth credential and auth router middleware.
- remove "is-base64" package.
- add "authSourceNormalizerMiddleware" test cases.
- add new test cases for testing auth api with providing query string or paylod auth data source.
… jest run correct.

- import 'koa-bodyparser' in each authenticator because koa-bodyparser has define it under the koa types in node_module.
- remove defined 'BodyRequest'
@kokokuo kokokuo changed the title Feature: provide auth identity route for client-server used. Feature: provide getting auth token and user profile endpoint api for client-server used. Sep 21, 2022
@kokokuo kokokuo changed the title Feature: provide getting auth token and user profile endpoint api for client-server used. Feature: provide getting auth token and user profile api for client-server used. Sep 21, 2022
@kokokuo
Copy link
Contributor Author

kokokuo commented Sep 21, 2022

Hi @oscar60310, it has been fixed and provide new API for getting user profile, thanks for discussing with me and giving suggestion 😃

@kokokuo kokokuo requested a review from oscar60310 September 21, 2022 07:37
…n options, and set disable for auth in "vulcan.yaml" template in labs project
@oscar60310 oscar60310 merged commit 2a5e525 into develop Sep 22, 2022
@oscar60310 oscar60310 deleted the feature/auth-route branch September 22, 2022 01:21
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

Successfully merging this pull request may close these issues.

3 participants