Skip to content

Commit 276849f

Browse files
authored
Merge branch 'develop' into robertlong/group-call
2 parents 3711899 + a57c430 commit 276849f

File tree

5 files changed

+129
-5
lines changed

5 files changed

+129
-5
lines changed

spec/unit/NamespacedValue.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,21 @@ describe("NamespacedValue", () => {
2121
const ns = new NamespacedValue("stable", "unstable");
2222
expect(ns.name).toBe(ns.stable);
2323
expect(ns.altName).toBe(ns.unstable);
24+
expect(ns.names).toEqual([ns.stable, ns.unstable]);
2425
});
2526

2627
it("should return unstable if there is no stable", () => {
2728
const ns = new NamespacedValue(null, "unstable");
2829
expect(ns.name).toBe(ns.unstable);
2930
expect(ns.altName).toBeFalsy();
31+
expect(ns.names).toEqual([ns.unstable]);
3032
});
3133

3234
it("should have a falsey unstable if needed", () => {
3335
const ns = new NamespacedValue("stable", null);
3436
expect(ns.name).toBe(ns.stable);
3537
expect(ns.altName).toBeFalsy();
38+
expect(ns.names).toEqual([ns.stable]);
3639
});
3740

3841
it("should match against either stable or unstable", () => {
@@ -58,12 +61,14 @@ describe("UnstableValue", () => {
5861
const ns = new UnstableValue("stable", "unstable");
5962
expect(ns.name).toBe(ns.unstable);
6063
expect(ns.altName).toBe(ns.stable);
64+
expect(ns.names).toEqual([ns.unstable, ns.stable]);
6165
});
6266

6367
it("should return unstable if there is no stable", () => {
6468
const ns = new UnstableValue(null, "unstable");
6569
expect(ns.name).toBe(ns.unstable);
6670
expect(ns.altName).toBeFalsy();
71+
expect(ns.names).toEqual([ns.unstable]);
6772
});
6873

6974
it("should not permit falsey unstable values", () => {

spec/unit/login.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { SSOAction } from '../../src/@types/auth';
12
import { TestClient } from '../TestClient';
23

34
describe('Login request', function() {
@@ -22,3 +23,37 @@ describe('Login request', function() {
2223
expect(client.client.getUserId()).toBe(response.user_id);
2324
});
2425
});
26+
27+
describe('SSO login URL', function() {
28+
let client: TestClient;
29+
30+
beforeEach(function() {
31+
client = new TestClient();
32+
});
33+
34+
afterEach(function() {
35+
client.stop();
36+
});
37+
38+
describe('SSOAction', function() {
39+
const redirectUri = "https://test.com/foo";
40+
41+
it('No action', function() {
42+
const urlString = client.client.getSsoLoginUrl(redirectUri, undefined, undefined, undefined);
43+
const url = new URL(urlString);
44+
expect(url.searchParams.has('org.matrix.msc3824.action')).toBe(false);
45+
});
46+
47+
it('register', function() {
48+
const urlString = client.client.getSsoLoginUrl(redirectUri, undefined, undefined, SSOAction.REGISTER);
49+
const url = new URL(urlString);
50+
expect(url.searchParams.get('org.matrix.msc3824.action')).toEqual('register');
51+
});
52+
53+
it('login', function() {
54+
const urlString = client.client.getSsoLoginUrl(redirectUri, undefined, undefined, SSOAction.LOGIN);
55+
const url = new URL(urlString);
56+
expect(url.searchParams.get('org.matrix.msc3824.action')).toEqual('login');
57+
});
58+
});
59+
});

src/@types/auth.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,66 @@ export interface IRefreshTokenResponse {
2727
}
2828

2929
/* eslint-enable camelcase */
30+
31+
/**
32+
* Response to GET login flows as per https://spec.matrix.org/latest/client-server-api/#get_matrixclientv3login
33+
*/
34+
export interface ILoginFlowsResponse {
35+
flows: LoginFlow[];
36+
}
37+
38+
export type LoginFlow = ISSOFlow | IPasswordFlow | ILoginFlow;
39+
40+
export interface ILoginFlow {
41+
type: string;
42+
}
43+
44+
export interface IPasswordFlow extends ILoginFlow {
45+
type: "m.login.password";
46+
}
47+
48+
/**
49+
* Representation of SSO flow as per https://spec.matrix.org/latest/client-server-api/#client-login-via-sso
50+
*/
51+
export interface ISSOFlow extends ILoginFlow {
52+
type: "m.login.sso" | "m.login.cas";
53+
// eslint-disable-next-line camelcase
54+
identity_providers?: IIdentityProvider[];
55+
}
56+
57+
export enum IdentityProviderBrand {
58+
Gitlab = "gitlab",
59+
Github = "github",
60+
Apple = "apple",
61+
Google = "google",
62+
Facebook = "facebook",
63+
Twitter = "twitter",
64+
}
65+
66+
export interface IIdentityProvider {
67+
id: string;
68+
name: string;
69+
icon?: string;
70+
brand?: IdentityProviderBrand | string;
71+
}
72+
73+
/**
74+
* Parameters to login request as per https://spec.matrix.org/latest/client-server-api/#login
75+
*/
76+
/* eslint-disable camelcase */
77+
export interface ILoginParams {
78+
identifier?: object;
79+
password?: string;
80+
token?: string;
81+
device_id?: string;
82+
initial_device_display_name?: string;
83+
}
84+
/* eslint-enable camelcase */
85+
86+
export enum SSOAction {
87+
/** The user intends to login to an existing account */
88+
LOGIN = "login",
89+
90+
/** The user intends to register for a new account */
91+
REGISTER = "register",
92+
}

src/NamespacedValue.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ export class NamespacedValue<S extends string, U extends string> {
4141
return this.unstable;
4242
}
4343

44+
public get names(): (U | S)[] {
45+
const names = [this.name];
46+
const altName = this.altName;
47+
if (altName) names.push(altName);
48+
return names;
49+
}
50+
4451
public matches(val: string): boolean {
4552
return this.name === val || this.altName === val;
4653
}

src/client.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,14 @@ import {
192192
} from "./webrtc/groupCall";
193193
import { MediaHandler } from "./webrtc/mediaHandler";
194194
import { GroupCallEventHandler } from "./webrtc/groupCallEventHandler";
195-
import { IRefreshTokenResponse } from "./@types/auth";
195+
import { ILoginFlowsResponse, IRefreshTokenResponse, SSOAction, IRefreshTokenResponse } from "./@types/auth";
196196
import { TypedEventEmitter } from "./models/typed-event-emitter";
197197
import { ReceiptType } from "./@types/read_receipts";
198198
import { MSC3575SlidingSyncRequest, MSC3575SlidingSyncResponse, SlidingSync } from "./sliding-sync";
199199
import { SlidingSyncSdk } from "./sliding-sync-sdk";
200200
import { Thread, THREAD_RELATION_TYPE } from "./models/thread";
201201
import { MBeaconInfoEventContent, M_BEACON_INFO } from "./@types/beacon";
202+
import { UnstableValue } from "./NamespacedValue";
202203
import { ToDeviceMessageQueue } from "./ToDeviceMessageQueue";
203204
import { ToDeviceBatch } from "./models/ToDeviceMessage";
204205
import { IgnoredInvites } from "./models/invites-ignorer";
@@ -897,6 +898,8 @@ export type ClientEventHandlerMap = {
897898
& HttpApiEventHandlerMap
898899
& BeaconEventHandlerMap;
899900

901+
const SSO_ACTION_PARAM = new UnstableValue("action", "org.matrix.msc3824.action");
902+
900903
/**
901904
* Represents a Matrix Client. Only directly construct this if you want to use
902905
* custom modules. Normally, {@link createClient} should be used
@@ -7179,10 +7182,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
71797182

71807183
/**
71817184
* @param {module:client.callback} callback Optional.
7182-
* @return {Promise} Resolves: TODO
7185+
* @return {Promise<ILoginFlowsResponse>} Resolves to the available login flows
71837186
* @return {module:http-api.MatrixError} Rejects: with an error response.
71847187
*/
7185-
public loginFlows(callback?: Callback): Promise<any> { // TODO: Types
7188+
public loginFlows(callback?: Callback): Promise<ILoginFlowsResponse> {
71867189
return this.http.request(callback, Method.Get, "/login");
71877190
}
71887191

@@ -7258,15 +7261,26 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
72587261
* @param {string} loginType The type of SSO login we are doing (sso or cas).
72597262
* Defaults to 'sso'.
72607263
* @param {string} idpId The ID of the Identity Provider being targeted, optional.
7264+
* @param {SSOAction} action the SSO flow to indicate to the IdP, optional.
72617265
* @return {string} The HS URL to hit to begin the SSO login process.
72627266
*/
7263-
public getSsoLoginUrl(redirectUrl: string, loginType = "sso", idpId?: string): string {
7267+
public getSsoLoginUrl(
7268+
redirectUrl: string,
7269+
loginType = "sso",
7270+
idpId?: string,
7271+
action?: SSOAction,
7272+
): string {
72647273
let url = "/login/" + loginType + "/redirect";
72657274
if (idpId) {
72667275
url += "/" + idpId;
72677276
}
72687277

7269-
return this.http.getUrl(url, { redirectUrl }, PREFIX_R0);
7278+
const params = {
7279+
redirectUrl,
7280+
[SSO_ACTION_PARAM.unstable!]: action,
7281+
};
7282+
7283+
return this.http.getUrl(url, params, PREFIX_R0);
72707284
}
72717285

72727286
/**

0 commit comments

Comments
 (0)