vardius / go-api-boilerplate

Go Server/API boilerplate using best practices DDD CQRS ES gRPC
https://go-api-boilerplate.local
MIT License
934 stars 137 forks source link

Input validation, sanitizing and form/multiform/files support, OAuth2 logic #52

Closed mar1n3r0 closed 4 years ago

mar1n3r0 commented 4 years ago

Continuing the topic here.

Because we are using CQRS patter, the way I think validation should be done is for value objects to validate incoming data.

So technically for example here:

// ChangeEmailAddress command
type ChangeEmailAddress struct {
  ID    uuid.UUID `json:"id"`
  Email string    `json:"email"`
}

each property should be a value object (VO), that validates data

// ChangeEmailAddress command
type ChangeEmailAddress struct {
  ID    uuid.UUID `json:"id"`
  Email vo.Email  `json:"email"`
}

this way you ensures that data passed to the events always conforms to the rules, i dont mind having different validation system that we could for example use in our VOs

but anyway lets first fix/merge current open prs and then we will consider other futures. scopes are good idea, i am not sure but i dont think we should need to do much here, it should work out of box with current oauth2 implementation

Originally posted by @vardius in https://github.com/vardius/go-api-boilerplate/issues/39#issuecomment-570094391

Maybe it's better to do the sanitizing and validation one step before. If we ensure this happens in BuildCommandDispatchHandler where we still have the request instead of doing it later in the NewCommandFromPayload then the CQRS structs will always get the sanitized/validated data. The earlier the better, less data moving down the pipe.

This has the advantage that we can reuse a mature open-source package which has additional benefits like adding form/multiform/files support.

What do you think @vardius ?

Edit: That looks like a great candidate for both json, text/plain and form data altogether:

govalidator

So far I got my eyes on: albrow/forms as the validation happens on the http.Request level.

But since last update was 2 years ago and it's not migrated to using modules we can take a look at other options as well: awesome-go forms

The previously mentioned ozzo-validation seems to be tailored to validation of value objects level so not really suited for use with the http package directly. This kind of takes it out of the equation for now I guess.

vardius commented 4 years ago

I don't mind having an additional layer, but still I believe VOs to be a source of truth. Most of times I use same VOs for commands and events, this way I ensure that events have always valid state as well.

Then for example if you want to replay or something like that, where the commands are excluded from the process you are sure the data is still correct, and technically should not cause any issues. Because VOs still validate it on the even level as well.

Form validation should also be done on the client side. In the end any solution should be fine as long as there is one source of truth, and all the cases are handled.

mar1n3r0 commented 4 years ago

It makes sense and also has native support in govalidator via the govalidator Struct Validation.

I have tried to explore this idea further, it's only a demo for now: #53

Do you think that's a good approach to it, can we optimize further before proceeding with the rest of the cases and tests ?

Edit: Explored the idea further. Do we have any examples of the social auth ? Using OAuth 2.0 to Access Google APIs Facebook Work with package golang.org/x/oauth2

From what I can see so far we need to use their official go-api-client rather than our OAuth2 server to connect because we are the clients.

Our OAuth2 server is not responsible for anything else but the internal registration right ? And then each social provider we connect to provides an official client to use for the connection to their OAuth2 server.

In fact maybe we can replace the whole Oauth2 server with something like this which can handle all social providers? gocialite

There are serious security flaws in gocialite and it seems non-maintained for almost a year but this should do the job: goth

Should reduce overhead quite dramatically and the internal registration can be handled directly with JWT as it is without the need for the OAuth2 server which would be needed only in case we were a provider to 3rd parties. This will in turn render the auth service not needed overall since the user service handles all of it.

Question:

By default, gothic uses a CookieStore from the gorilla/sessions package to store session data.

Do you think we can use the eventStore for that ? It is needed for the state query param which serves as the CSRF token between beginAuth process and the callback.

vardius commented 4 years ago

I have added oauth2 here for feature, in case of authenticating other external services agains our auth service, seems for me natural to use it for user authentication as well then. our services are authenticated agains auth service using static client as well.

authenticating using social auth gives us external token which we use later fetch user data and to obtain our oauth2 token which allows user to use our services.

mar1n3r0 commented 4 years ago

Yeah it makes perfect sense for when external services need the oauth2 server on our side.

For social auth though it doesn't need a server but a client library only. Example: 45ebb3a68db284c09d4c262b707b408f2498fd59

Goth is basically standardizing the retrieval of the access token and the user profile data so we can all store it in users in one step. The only difference between internal and external registration being that for internal we have a password which is null for social auth. At the same time The userId field is provider's userId so it's always null for internal registrations since we use id for that. Upon auth with social provider we keep both id and userId so we "register" it in our system.

Goth is using only handlers in the form of client implementations for all providers. Hence allowing us to not use our auth service for that and do it directly from the user.

Following that same thought do you think we can keep the oauth2 server but disconnect it from the flow until needed(for 3rd parties) so we can basically store everything in User model while using something like this for the internal registration: jwt-go ?

That would completely decouple the oauth2 server since we have jwt-go in the user service for the internal registration, should simplify the overall flow and improve performance since we are not calling a second service.

Edit: A few things which I think are worth discussing.

  1. We don't need to store the access tokens in the database since they are crypto verifiable with the app secret.

  2. We don't need to store refresh token for internal registration with email since we can issue a new token based on the old one.

  3. We need to store the refresh token from the providers because it's the only way to get a new access token through their authorization server.

  4. The original implementation of social auth was issuing our access token upon receiving the provider ones. This defeats the purpose of using oauth2 since we don't really use their token but rather issue ours if they send us theirs.

It turns out there is a way to validate their token like this:

google-auth-id-token-verifier

I believe this is supposed to be used alongside the middleware for our internal tokens so that we check if the token is ours if not try to validate with google etc. This way when we authenticate with a provider we actually continue to use their access token for authentication in our app.

vardius commented 4 years ago

How I think social auth should work, considering we develop api - client infrastructure is:

also i rather not use 3rd party tokens as a way for authorisation within our system, i believe the right way to do it is as i listed above, issue our token, and user 3rd party to fetch user info only while registering. this way we are not dependent on 3rd party service being up and running, if they are down our app still works as we need to fetch data only once during register process

we should keep external dependencies to minimum, for our system to be running

vardius commented 4 years ago

@mar1n3r0 have a look at this, https://github.com/vardius/go-api-boilerplate/pull/58 some example on how i imagine value objects thing to look like

mar1n3r0 commented 4 years ago

some example on how i imagine value objects thing to look like

Looks very suitable for cqrs as you mentioned.

How I think social auth should work, considering we develop api - client infrastructure is:

client (react, angular, vue, mobile app) ask user for login, passes auth token to api (our boilerplate) our api fetches user info using auth token adds new user generates our ouath2 token sends emails with token for user to sign in (as a security step) also i rather not use 3rd party tokens as a way for authorisation within our system, i believe the right way to do it is as i listed above, issue our token, and user 3rd party to fetch user info only while registering. this way we are not dependent on 3rd party service being up and running, if they are down our app still works as we need to fetch data only once during register process

we should keep external dependencies to minimum, for our system to be running

Probably I misunderstood the concept from the very start. So the client sends the clientId and the redirectUri and the api holds the clientSecret.

That makes sense now. When the callback receives the token don't we still need to validate the token once to ensure it is indeed coming from the provider since the callback is a public route and everyone can send any token to it ?

facebook login flow

Looking at the flow here doesn't make it completely clear.

Invoking the Login Dialog and Setting the Redirect URL Your app must initiate a redirect to an endpoint which will display the login dialog:

https://www.facebook.com/v6.0/dialog/oauth?
  client_id={app-id}
  &redirect_uri={redirect-uri}
  &state={state-param}

That looks ok and can be done from the client side. But then:

Exchanging Code for an Access Token To get an access token, make an HTTP GET request to the following OAuth endpoint:

GET https://graph.facebook.com/v6.0/oauth/access_token?
   client_id={app-id}
   &redirect_uri={redirect-uri}
   &client_secret={app-secret}
   &code={code-parameter}

Can this be done from the client since it can not store the secret securely ?

facebook access-tokens

Note that because this request uses your app secret, it must never be made in client-side code or in an app binary that could be decompiled. It is important that your app secret is never shared with anyone. Therefore, this API call should only be made using server-side code.

And then here is the validation as per their best practices where we need to make sure this token is actually the real token. Interestingly enough this is a second token called input_token.

Inspecting Access Tokens Whether or not your app uses code or token as your response_type from the Login dialog, it will have received an access token. You can perform automated checks on these tokens using a Graph API endpoint:

GET graph.facebook.com/debug_token?
     input_token={token-to-inspect}
     &access_token={app-token-or-admin-token}
This endpoint takes the following parameters:

The token verification appears to be not needed thanks to the csrf protection that the state param provides. We are always sure that the callback is part of the same session and coming from the AuthURL so should be able to safely issue our token without any verification of theirs.

mar1n3r0 commented 4 years ago

@vardius We can close this one if you want. It become quite convoluted anyway ?