Skip to content

Commit

Permalink
Additional authentication headers handling (#10)
Browse files Browse the repository at this point in the history
* Add custom news headers

* Bump actions/checkout from 2 to 3.1.0 (#5)

Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.1.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v2...v3.1.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump dependabot/fetch-metadata from 1.3.3 to 1.4.0 (#8)

Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.3 to 1.4.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](dependabot/fetch-metadata@v1.3.3...v1.4.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* - Authentication with Bearer token
- Improved CSRF token handling

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Ezequiel Dias <ezequidias@outlook.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed May 27, 2023
1 parent fd4d3b2 commit 3c2b564
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 30 deletions.
5 changes: 3 additions & 2 deletions src/channel-auth.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import request from "./util/request";

import { EventSourceConnection } from "./EventSourceConnection";
import { Options } from './echo-broadcaster/wave-connector';

export interface AuthRequest {
after: (callback: Function) => AuthRequest;
response: Promise<void>;
}

export function authRequest(channel: string, connection: EventSourceConnection, authEndpoint = '/broadcasting/auth'): AuthRequest {
export function authRequest(channel: string, connection: EventSourceConnection, options: Options): AuthRequest {
let authorized = false;
let afterAuthCallbacks: Function[] = [];

Expand All @@ -23,7 +24,7 @@ export function authRequest(channel: string, connection: EventSourceConnection,
return this;
}

const response = request(connection).post(authEndpoint, { channel_name: channel }).then((response) => {
const response = request(connection).post(options.authEndpoint, options, { channel_name: channel }).then((response) => {
authorized = true;

afterAuthCallbacks.forEach((callback) => callback(response));
Expand Down
13 changes: 12 additions & 1 deletion src/echo-broadcaster/wave-connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,23 @@ import WaveChannel from './wave-channel';
import WavePrivateChannel from './wave-private-channel';
import WavePresenceChannel from './wave-presence-channel';

export interface Options {
endpoint: string,
auth: {
headers: Record<string, string>,
},
authEndpoint: string,
csrfToken?: string,
bearerToken?: string,
namespace: string,
}

export class WaveConnector extends Connector {
private connection: EventSourceConnection;

private channels: Record<string, WaveChannel | WavePresenceChannel> = {};

constructor(options: any) {
constructor(options: Options) {
super({ endpoint: '/wave', ...options });
}

Expand Down
6 changes: 3 additions & 3 deletions src/echo-broadcaster/wave-presence-channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default class WavePresenceChannel extends WavePrivateChannel implements P
constructor(connection, name, options) {
super(connection, name, options);

this.joinRequest = authRequest(name, connection, this.options.endpoint + '/presence-channel-users')
this.joinRequest = authRequest(name, connection, { ...this.options, authEndpoint: this.options.endpoint + '/presence-channel-users' })
.after(() => this.joined = true);

if (typeof window !== 'undefined') {
Expand All @@ -23,7 +23,7 @@ export default class WavePresenceChannel extends WavePrivateChannel implements P
public here(callback: Function): WavePresenceChannel {
if (this.joined) {
request(this.connection)
.get(this.options.endpoint + '/presence-channel-users', { channel_name: this.name })
.get(this.options.endpoint + '/presence-channel-users', this.options, { channel_name: this.name })
.then((users) => callback(users));

return this;
Expand Down Expand Up @@ -54,7 +54,7 @@ export default class WavePresenceChannel extends WavePrivateChannel implements P

public unsubscribe(): void {
this.joinRequest.after(() => {
request(this.connection).delete(this.options.endpoint + '/presence-channel-users', { channel_name: this.name });
request(this.connection).delete(this.options.endpoint + '/presence-channel-users', this.options, { channel_name: this.name });
super.unsubscribe();
});
}
Expand Down
5 changes: 2 additions & 3 deletions src/echo-broadcaster/wave-private-channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import request from '../util/request';
import { AuthRequest, authRequest } from '../channel-auth';

import WaveChannel from './wave-channel';
import { Channel } from 'laravel-echo';

export default class WavePrivateChannel extends WaveChannel {
protected authorized = false;
Expand All @@ -16,11 +15,11 @@ export default class WavePrivateChannel extends WaveChannel {
constructor(connection, name, options) {
super(connection, name, options);

this.auth = authRequest(name, connection, this.options.authEndpoint);
this.auth = authRequest(name, connection, this.options);
}

public whisper(eventName, data) {
request(this.connection).post(this.options.endpoint + '/whisper', { channel_name: this.name, event_name: eventName, data });
request(this.connection).post(this.options.endpoint + '/whisper', this.options, { channel_name: this.name, event_name: eventName, data });

return this;
}
Expand Down
19 changes: 17 additions & 2 deletions src/echo/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ export abstract class Connector {
headers: {},
},
authEndpoint: '/broadcasting/auth',
userAuthentication: {
endpoint: '/broadcasting/user-auth',
headers: {},
},
broadcaster: 'pusher',
csrfToken: null,
bearerToken: null,
host: null,
key: null,
namespace: 'App.Events',
Expand All @@ -35,8 +40,18 @@ export abstract class Connector {
protected setOptions(options: any): any {
this.options = Object.assign(this._defaultOptions, options);

if (this.csrfToken()) {
this.options.auth.headers['X-CSRF-TOKEN'] = this.csrfToken();
let token = this.csrfToken();

if (token) {
this.options.auth.headers['X-CSRF-TOKEN'] = token;
this.options.userAuthentication.headers['X-CSRF-TOKEN'] = token;
}

token = this.options.bearerToken;

if (token) {
this.options.auth.headers['Authorization'] = 'Bearer ' + token;
this.options.userAuthentication.headers['Authorization'] = 'Bearer ' + token;
}

return options;
Expand Down
15 changes: 10 additions & 5 deletions src/util/request.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { EventSourceConnection } from "../EventSourceConnection";
import { Options } from '../echo-broadcaster/wave-connector';

export default function request(connection: EventSourceConnection) {
function create(method: 'GET' | 'POST' | 'PUT' | 'DELETE', route: string, data?: object): Promise<Response> {
let csrfToken = null;
if (typeof document !== 'undefined') {
function create(method: 'GET' | 'POST' | 'PUT' | 'DELETE', route: string, options: Options, data?: object): Promise<Response> {
let csrfToken = '';

if (typeof document !== 'undefined' && options.auth.headers['X-CSRF-TOKEN'] === undefined) {
const match = document.cookie.match(new RegExp('(^|;\\s*)(XSRF-TOKEN)=([^;]*)'));
csrfToken = match ? decodeURIComponent(match[3]) : null;
}

const csrfTokenHeader = csrfToken ? {'X-XSRF-TOKEN': csrfToken} : {}

const fetchRequest = (connectionId) => fetch(route, {
method: method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Socket-Id': connectionId,
'X-XSRF-TOKEN': csrfToken,
...options.auth.headers,
...csrfTokenHeader,
},
body: JSON.stringify(data || {}),
}).then((response) => {
Expand All @@ -28,7 +33,7 @@ export default function request(connection: EventSourceConnection) {
}) : fetchRequest(connection.getId());
}

const factory = (method: 'GET' | 'POST' | 'PUT' | 'DELETE') => (route: string, data?: object) => create(method, route, data);
const factory = (method: 'GET' | 'POST' | 'PUT' | 'DELETE') => (route: string, options: Options, data?: object) => create(method, route, options, data);

return {
get: factory('GET'),
Expand Down
15 changes: 7 additions & 8 deletions src/wave-broadcaster.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { EventSourceConnection } from "./EventSourceConnection";

import { WaveModel } from "./wave-model";

interface Options {
endpoint: string;
authEndpoint: string;
namespace: string;
}
import { Options } from './echo-broadcaster/wave-connector';

export class Wave {
protected connection: EventSourceConnection;
Expand All @@ -15,7 +10,12 @@ export class Wave {

private options: Options = {
endpoint: '/wave',
auth: {
headers: {},
},
authEndpoint: '/broadcasting/auth',
csrfToken: null,
bearerToken: null,
namespace: 'App.Models',
}

Expand All @@ -29,8 +29,7 @@ export class Wave {
const index = `${model}.${String(key)}`;

if (!this.models[index]) {
const { authEndpoint, namespace } = this.options;
this.models[index] = new WaveModel(model, key, this.connection, { authEndpoint, namespace });
this.models[index] = new WaveModel(model, key, this.connection, this.options);
}

return this.models[index];
Expand Down
8 changes: 2 additions & 6 deletions src/wave-model.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { EventSourceConnection } from "./EventSourceConnection";
import { authRequest, AuthRequest } from "./channel-auth";

interface Options {
namespace: string,
authEndpoint:string,
}
import { Options } from './echo-broadcaster/wave-connector';

const authMethods = [
'created',
Expand Down Expand Up @@ -40,7 +36,7 @@ export class WaveModel {

const channelName = `${this.options.namespace}.${this.name}.${this.key}`;

this.auth = authRequest(channelName, connection, this.options.authEndpoint);
this.auth = authRequest(channelName, connection, this.options);

this.channel = `private-${channelName}`;

Expand Down

0 comments on commit 3c2b564

Please sign in to comment.