Reputation: 3347
I have an angular 6 app & python(django 2.0.6) back-end. Angular 6 app is deployed using AWS S3 & CloudFront.
Angular 6 app use websocket functionality. Everything works well locally, but when deployed Angular 6 app fails to pass a cookies with websocket handshake request. (Authorization is by cookies only for websoket connection. There are reasons for that, not relevant.)
Websocket server located at subdomain: `api.site.com``
i tried multiple ways to set cookies in angular 6 app, such as:
let domain = 'api.site.com'
document.cookie = `Token=${token}; domain=${domain}; path=/`;
document.cookie = `Token=${token}; path=/`;
document.cookie = `Token=${token};
document.cookie = `Token=${token}, domain=${domain}, path=/`;
using ngx-cookie-service
:
this.cookieService.set('Token', token, undefined, '/', 'api.site.com');
this.cookieService.set('Token', token, undefined, '/');
this.cookieService.set('Token', token);
token
is a JWT token.
For all the cases above, Token
cookie does not passed with the websocket handshake when the app is deployed. using wss
protocol.
angular 6 app runs under the domain site.com
(=> api
is a subdomain )
Cloudfront behavior is set to pass all cookies.
Please advice what can be a possible reason.
Upvotes: 3
Views: 2769
Reputation: 3347
Solution:
In my case i wasn't able to set the cookie, but made a solution based on based on this topic: HTTP headers in Websockets client API
There is a lot of scattered information on how to authenticate WebSocket client API with jwt token. Here is the complete solution which is valid for django 2.0.6
, channels 2.1.1
, angular 6
stack:
django middleware:
class TokenAuthMiddleware:
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
auth_header = None
if 'subprotocols' in scope:
try:
auth_header = scope['subprotocols'][1]
except:
pass
if auth_header:
try:
user_jwt = jwt.decode(
auth_header,
settings.SECRET_KEY,
)
scope['user'] = MyUser.objects.get(
id=user_jwt['user_id']
)
close_old_connections()
except (InvalidSignatureError, KeyError, ExpiredSignatureError, DecodeError):
scope['auth_error'] = 'KeyError'
pass
except Exception as e: # NoQA
scope['auth_error'] = 'Unknown'
return self.inner(scope)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
ws consumer:
class WsConsumer(JsonWebsocketConsumer):
def connect(self):
self.accept('auth_token')
if self._is_authenticated():
***do things***
else:
logger.error("ws client auth error")
self.close(code=4003)
def _is_authenticated(self):
if hasattr(self.scope['headers'], 'auth_error'):
return False
if type(self.scope['user']) is AnonymousUser or not self.scope['user']:
return False
return True
WebSocket client API (js cli):
this.socket = new WebSocket('ws://host.com/ws/`, ['auth_token', token]);
Upvotes: 2