save-cloud icon indicating copy to clipboard operation
save-cloud copied to clipboard

Enhance authorization and authentication in save-cloud

Open kgevorkyan opened this issue 2 years ago • 9 comments

TODO:

  • [x] test API after changing
    • https://github.com/saveourtool/save-backend-tests/pull/71
  • [x] #2386
    • refactor BackendService and receive save's entity\class instead of spring's one to allow to provide ID
    • refactor api-gateway and authentication-service to pass user's ID from api-gateway instead of second fetch to database in authentication-service's logic
    • removed using Authentication: Basic ***:*** in authentication-service -- we can create save's token with required info without checking ReactiveUserDetailsService. We can try to use org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken. It can be easy to migrate to X.509 authentication in future
  • [ ] Update swagger\open-api annotations:
    • remove com.saveourtool.save.configs.RequiresAuthorizationSourceHeader or maybe leave because it's useful for local testing
    • update com.saveourtool.save.configs.ApiSwaggerSupport, we support oauth2 and basic auth
  • [ ] cover by tests
  • [ ] #2407
  • [x] #2631
  • [x] #2629
  • [x] #2633
  • [ ] #2540

Separator for user and auth provider should be revised

For now we are using @ for separate the user name and auth provider at least in WebSecurityConfig and in UserUtils.kt

But this symbol is not the variable and just the hardcoded symbol, so its hard to maintain this functionality

First of all, it should be extracted into the variable in save-cloud-common

And after this, we probably will need to change @ to something else, since @ is too dangerous to be a spec symbol

This work should be done under separate issue and very carefully, since it influence on core functionality - API application, auth. integration tests, db

kgevorkyan avatar Jul 14 '23 12:07 kgevorkyan

CRITICAL thing we have just found:

  1. User VLAD comes from Github
  2. Initially spring register him as VLAD@GITHUB
  3. Then he is redirected to a registration view, where he CAN change VLAD name to ANDREY
  4. ANDREY comes from GITHUB
  5. Now ANDREY is not even able to press OAuth registration button

orchestr7 avatar Jul 14 '23 12:07 orchestr7

That's why we need to GENERATE a our own SAVE ID for user when he first time register!

orchestr7 avatar Jul 14 '23 12:07 orchestr7

seems, that @ should not influent on problems with email at all, since its only used in basic auth, but should be extracted anyway

kgevorkyan avatar Jul 17 '23 14:07 kgevorkyan

        .httpBasic { httpBasicSpec ->
            // Authenticate by comparing received basic credentials with existing one from DB
            httpBasicSpec.authenticationManager(
                UserDetailsRepositoryReactiveAuthenticationManager { username ->
                    // Looking for user in DB by received source and name
                    require(username.contains("@")) {
                        "Provided user information should keep the following form: oauth2Source@username"
                    }

Also be careful with Basic oauth

Cheshiriks avatar Jul 19 '23 09:07 Cheshiriks

That's why we need to GENERATE a our own SAVE ID for user when he first time register!

    fun findByName(name: String): User? {
        val record = namedParameterJdbcTemplate.queryForList(
            "SELECT * FROM save_cloud.user WHERE name = :name",
            mapOf("name" to name)
        ).singleOrNull() ?:
        namedParameterJdbcTemplate.queryForList(
            "SELECT * FROM save_cloud.user WHERE id = (select user_id from save_cloud.original_login where name = :name)",
            mapOf("name" to name)
        ).singleOrNull()
            .orNotFound {
                "There is no user with name $name"
            }
        return record.toUserEntity()
    }

Cheshiriks avatar Jul 19 '23 10:07 Cheshiriks

For history

A&A in save-cloud:

Basic auth:

  1. Request comes to api-gateway with header Authorization: Basic ***:***
  2. api-gateway has UserDetailsRepositoryReactiveAuthenticationManager (com.saveourtool.save.gateway.security.WebSecurityConfig) which uses internal endpoint in backend to get all entities from user with user.name, user.password (token) and user.role.
  3. Authenticated request comes then to com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactory which adds a new header Authorization to request to backend. It passes only user name without password.
  4. Backend uses com.saveourtool.save.authservice.security.ConvertingAuthenticationManager to authenticate: it checks only username

OAUTH2

  1. Request comes to api-gateway after successful authentication on oauth2 server (github, google and etc).
  2. Authenticated request comes then to com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactory which searches user.name via internal endpoint in backend and adds a new header Authorization to request to backend. It passes only user name without password.
  3. Backend uses com.saveourtool.save.authservice.security.ConvertingAuthenticationManager to authenticate: it checks only username

nulls avatar Jul 21 '23 09:07 nulls

For history

A&A in save-cloud:

Basic auth:

  1. Request comes to api-gateway with header Authorization: Basic ***:***
  2. api-gateway has UserDetailsRepositoryReactiveAuthenticationManager (com.saveourtool.save.gateway.security.WebSecurityConfig) which uses internal endpoint in backend to get all entities from user with user.name, user.password (token) and user.role.
  3. Authenticated request comes then to com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactory which adds a new header Authorization to request to backend. It passes only user name without password.
  4. Backend uses com.saveourtool.save.authservice.security.ConvertingAuthenticationManager to authenticate: it checks only username

OAUTH2

  1. Request comes to api-gateway after successful authentication on oauth2 server (github, google and etc).
  2. Authenticated request comes then to com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactory which searches user.name via internal endpoint in backend and adds a new header Authorization to request to backend. It passes only user name without password.
  3. Backend uses com.saveourtool.save.authservice.security.ConvertingAuthenticationManager to authenticate: it checks only username

may be add in readme, into some dev guide?

kgevorkyan avatar Jul 21 '23 11:07 kgevorkyan

For history

A&A in save-cloud:

Basic auth:

  1. Request comes to api-gateway with header Authorization: Basic ***:***
  2. api-gateway has UserDetailsRepositoryReactiveAuthenticationManager (com.saveourtool.save.gateway.security.WebSecurityConfig) which uses internal endpoint in backend to get all entities from user with user.name, user.password (token) and user.role.
  3. Authenticated request comes then to com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactory which adds a new header Authorization to request to backend. It passes only user name without password.
  4. Backend uses com.saveourtool.save.authservice.security.ConvertingAuthenticationManager to authenticate: it checks only username

OAUTH2

  1. Request comes to api-gateway after successful authentication on oauth2 server (github, google and etc).
  2. Authenticated request comes then to com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactory which searches user.name via internal endpoint in backend and adds a new header Authorization to request to backend. It passes only user name without password.
  3. Backend uses com.saveourtool.save.authservice.security.ConvertingAuthenticationManager to authenticate: it checks only username

may be add in readme, into some dev guide?

will do, when finish with this issue

nulls avatar Jul 21 '23 11:07 nulls

TODO:

  • [ ] test API after changing
  • [ ] refactor BackendService and receive save's entity\class instead of spring's one to allow to provide ID
  • [ ] refactor api-gateway and authentication-service to pass user's ID from api-gateway instead of second fetch to database in authentication-service's logic
  • [ ] removed using Authentication: Basic ***:*** in authentication-service -- we can create save's token with required info without checking ReactiveUserDetailsService. We can try to use org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken. It can be easy to migrate to X.509 authentication in future. Spring boot doesn't support it out of box. Need to implement custom WebFilter
  • [ ] cover by tests
  • [ ] update readme

nulls avatar Jul 24 '23 09:07 nulls