Iris: Websocket authentication in iris using JWT

Created on 27 Mar 2019  路  7Comments  路  Source: kataras/iris

How Websocket authentication in iris using JWT ?

How can I set a header?

question

All 7 comments

You have access to the Context via the websocket#Connection interface but if you want to do some operation before even the http upgrade to websocket protocol then you can use the connection = websocket/server#Upgrade(context) instead of the websocket/Server#OnConnection(func(connection)).

s := websocket.New(websocket.Config{...})
app.Get("/websocket_endpoint", func(ctx iris.Context){
  // ___AUTHENTICATION CODE HERE___
  // ...

  c := s.Upgrade(ctx)
  if c.Err() != nil {
    // handle upgrade error.
  }

  // do the same things as you normally do in your `OnConnection`.
  // ... i.e:
 c.On("chat", func(msg string) {
   fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg)
   c.To(websocket.Broadcast).Emit("chat", msg)
 })

  // and in the end you must fire:
  c.Wait()
})

Update

Read this comment: https://github.com/kataras/iris/issues/1229#issuecomment-508894309 instead

Thank you .
Just Two points.
1 - please see https://github.com/auth0-community/socketio-jwt

And https://github.com/gorilla/websocket/issues/497

2 - In func dial

func Dial(ctx stdContext.Context, url string, cfg ConnectionConfig) (ClientConnection, error) {
    if ctx == nil {
        ctx = stdContext.Background()
    }

    if !strings.HasPrefix(url, "ws://") && !strings.HasPrefix(url, "wss://") {
        url = "ws://" + url
    }

    conn, _, err := websocket.DefaultDialer.DialContext(ctx, url, nil)
    if err != nil {
        return nil, err
    }

    clientConn := WrapConnection(conn, cfg)
    go clientConn.Wait()

    return clientConn, nil
}

can not set header . websocket.DefaultDialer.DialContext(ctx, url, nil)

header is nil .

This is for the go client-side on the v11.2 which is not completed yet (the new websocket will be something like: https://github.com/kataras/neffos), right?

Yes . Thank you .

Hey @majidbigdeli I've just improved the v11.2 websocket example to handle jwt auth for server-side and browser clients. In Iris and neffos things are simplier than the links you suggested me to look above and even myself found them confusing and unnecessary complexity from the socket.io team.

You can check for authentication and force-close a client at any state[ at any event or on handshake]. I recommend to do client/request authentications on handshake http request itself as you can see on the example but it really depends on your app's flow and needs.

  1. You create the jwt middleware as you normally do with the iris-contrib/middleware/jwt (in the example the token extractor is done by a "token" url parameter and example clients are designed to set that with an example payload signed token)

Example Code:

import (
    // [...]
    "github.com/dgrijalva/jwt-go"
    jwtmiddleware "github.com/iris-contrib/middleware/jwt"
)

// [...]

j := jwtmiddleware.New(jwtmiddleware.Config{
    // Extract by the "token" url,
    // so the client should dial with ws://localhost:8080/echo?token=$token
    Extractor: jwtmiddleware.FromParameter("token"),
    ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
    return []byte("My Secret"), nil
    },
    SigningMethod: jwt.SigningMethodHS256,
})
  1. To register it before/on handshake (before websocket communication): You add the middleware to the websocket endpoint route, as you normally do, before the websocket.Handler(websocketServer)

Example Code:

// [websocketServer := neffos.New....]
app.Get("/echo", j.Serve, websocket.Handler(websocketServer))
  1. To register it on an event you do that by the jwt middleware's CheckJWT method and return any error to the event callback.

Example Code:

// [websocketServer := neffos.New....]
websocketServer.OnConnect = func(c *neffos.Conn) error {
    ctx := websocket.GetContext(c)
    if err := j.CheckJWT(ctx); err != nil {
        // will send the above error on the client
        // and will not allow it to connect to the websocket server at all.
        return err
    }
    log.Printf("[%s] connected to the server", c.ID())
    return nil
}
  1. To get the claims, the payload inside a websocket event callback:

    • when you have access to the jwt middleware value you can just use its Get

    ctx := websocket.GetContext(...)
    user :=  j.Get(ctx)
  • otherwise use the ctx.Values().Get("jwt") which is the key that the *jwt.Token value is stored on authenticated requests:
func (nsConn *neffos.NSConn, msg neffos.Message) error {
    ctx := websocket.GetContext(nsConn.Conn)
    user := ctx.Values().Get("jwt").(*jwt.Token)

    log.Printf("This is an authenticated request\n")
    log.Printf("Claim content:")
    log.Printf("%#+v\n", user.Claims)
    // [...]
}

And the browser-side may look something like this:

var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU";
var wsURL = "ws://localhost:8080/echo?token=" + token;
// [...]
try {
    var conn = await neffos.dial(wsURL, { namespace: events...});
} catch(err) { }

Hope that helped you and everyone else looking for a clear answer about this.

@kataras
Yes . It helps me a lot. I am really grateful .
This is a great feature.
Thank You ,Thank You,Thank You

You 're welcome @majidbigdeli I am really grateful for your comments, thank YOU and for giving me the opportunity to include more helpful examples for all of us!

Was this page helpful?
0 / 5 - 0 ratings