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

Support for json web token? #177

Open
EricDeng1001 opened this issue Aug 21, 2024 · 8 comments
Open

Support for json web token? #177

EricDeng1001 opened this issue Aug 21, 2024 · 8 comments
Assignees
Labels
feature New feature or request

Comments

@EricDeng1001
Copy link

I have a set of Service that must be used after login. I issue jwt to client so they can identify themselves. And I also got a AuthService which issue this token. So how do I setup my rpc server and client for this scenario? For example, how do I carry this jwt in client, and how do I verify this jwt in server? Besides, do I need to setup two paths, one for AuthService, because it does not require jwt, and another for rest of services, which requires jwt?

@EricDeng1001 EricDeng1001 added the feature New feature or request label Aug 21, 2024
@Mr3zee
Copy link
Collaborator

Mr3zee commented Aug 21, 2024

Hi! If you are using kRPC protocol with Ktor integration - they have a JWT plugin for this: https://ktor.io/docs/server-jwt.html#authenticate-route

Otherwise - no builtin solution is available

@EricDeng1001
Copy link
Author

Hello! I am already using this, but encounter a problem: when user not logged in, trying to connect ws returns 401, and service creation is failed. But I must have init these service when my app start.
Another problem is, it seems that rpc request is not carrtying bearer token like normal http call.
I will post my config code below.

@EricDeng1001
Copy link
Author

EricDeng1001 commented Aug 21, 2024

client:

object RPC {
    private val client = HttpClient {
        installRPC()
        install(Auth) {
            bearer {
                loadTokens {
                    BearerTokens(UserContext.token, "")
                }
                refreshTokens {
                    BearerTokens(UserContext.token, "")
                }
            }
        }
    }
    private lateinit var rpcClient: RPCClient
    lateinit var authService: AuthService
    lateinit var subscriptionManager: SubscriptionManager
    lateinit var channelManager: ChannelManager
    lateinit var purchaseService: PurchaseService
    lateinit var accountManager: AccountManager
    lateinit var pushManager: PushManager

    suspend fun init() {
        rpcClient = client.rpc {
            url("ws://localhost:3000/api")

            rpcConfig {
                serialization {
                    json()
                }
            }
        }
        authService = client.rpc {
            url("ws://localhost:3000/api/auth")

            rpcConfig {
                serialization {
                    json()
                }
            }
        }.withService()
        subscriptionManager = rpcClient.withService()
        channelManager = rpcClient.withService()
        purchaseService = rpcClient.withService()
        accountManager = rpcClient.withService()
        pushManager = rpcClient.withService()
    }
}

And init is called when App start using LauchedEffect.
server:

fun Application.module() {
    install(CORS) {
        allowOrigins { true }
    }
    install(RPC) {

    }
    install(Authentication) {
        jwt {
            verifier(
                AuthContext.verifier
            )
            validate { credential ->
                AuthContext.validate(credential)
            }
            challenge { defaultScheme, realm ->
                call.respond(HttpStatusCode.Unauthorized, "Token is not valid or has expired")
            }
            realm = "Access to maotao"
        }
    }
    install(DoubleReceive)
    install(CallLogging) {
        level = Level.INFO
        format { call ->
            runBlocking {
                val status = call.response.status()
                val httpMethod = call.request.httpMethod.value
                val userAgent = call.request.headers["User-Agent"]
                val body = call.receiveText()
                val uri = call.request.uri
                "$httpMethod $uri $status \n$userAgent \n$body"
            }
        }
    }


    routing {
        pwaDispatch()
        auth()
        authenticate {
            api()
        }
    }
}

And when app is started, server logged:

2024-08-21 21:12:50.065 [eventLoopGroupProxy-4-1] INFO  Application - GET /api 401 Unauthorized 
Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1 Edg/127.0.0.0 

@EricDeng1001
Copy link
Author

I was using REST before, and auth is 100% ok. I just switch from normal ktor to rpc.

@Mr3zee
Copy link
Collaborator

Mr3zee commented Aug 21, 2024

@EricDeng1001 your auth service is under url("ws://localhost:3000/api/auth") protected route:

authenticate {
    api()
}

Hence the error. You can try to move auth service into another route, which is not protocted.
At least that is what I understood from the Ktor docs

@Mr3zee
Copy link
Collaborator

Mr3zee commented Aug 27, 2024

@EricDeng1001 were you able to resolve the issue?

@EricDeng1001
Copy link
Author

I will try to add headers and see. Will report asap.

@EricDeng1001
Copy link
Author

Need newest patch to test it in production env. Waiting for it.

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

No branches or pull requests

2 participants