Skip to content

Commit 0c2617d

Browse files
authored
STITCH-3383 add client sdk FunctionCredential (#329)
1 parent e75a2ea commit 0c2617d

File tree

8 files changed

+279
-5
lines changed

8 files changed

+279
-5
lines changed

packages/browser/coretest/__tests__/StitchAppClientIntTests.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
Anon,
2222
App,
2323
AppResource,
24+
CustomFunctionProvider,
25+
CustomFunctionProviderConfig,
2426
CustomProvider,
2527
CustomProviderConfig,
2628
StitchFunction,
@@ -33,12 +35,15 @@ import {
3335
AnonymousCredential,
3436
CustomAuthProvider,
3537
CustomCredential,
38+
FunctionAuthProvider,
39+
FunctionCredential,
3640
MemoryStorage,
3741
StitchClientError,
3842
StitchClientErrorCode,
3943
UserPasswordAuthProvider,
4044
UserPasswordCredential,
41-
UserType
45+
UserType,
46+
StitchServiceError
4247
} from "mongodb-stitch-core-sdk";
4348

4449
const harness = new BaseStitchBrowserIntTestHarness();
@@ -520,6 +525,62 @@ describe("StitchAppClient", () => {
520525
expect(client.auth.listUsers().length).toBe(2);
521526
});
522527

528+
it("should authenticate using a custom function", async () => {
529+
const { app: appResponse, appResource: app } = await harness.createApp();
530+
531+
const def = await app.functions.create(new StitchFunction(
532+
"funkyAuth",
533+
false,
534+
`
535+
exports = function(payload) {
536+
return payload.id;
537+
}`
538+
));
539+
540+
await harness.addProvider(app, new CustomFunctionProvider(new CustomFunctionProviderConfig(def.id!!, def.name)));
541+
542+
const client = harness.getAppClient(appResponse);
543+
544+
const id = "123abc";
545+
const user = await client.auth.loginWithCredential(new FunctionCredential({id}));
546+
547+
expect(user).toBeDefined();
548+
549+
expect(user.id).toBeDefined();
550+
expect(user.identities[0].id).toBeDefined();
551+
expect(id).toEqual(user.identities[0].id);
552+
expect(FunctionAuthProvider.DEFAULT_NAME).toEqual(user.loggedInProviderName);
553+
expect(FunctionAuthProvider.TYPE).toEqual(user.loggedInProviderType);
554+
expect(UserType.Normal).toEqual(user.userType);
555+
expect(FunctionAuthProvider.TYPE).toEqual(user.identities[0].providerType);
556+
expect(client.auth.isLoggedIn).toBeTruthy();
557+
});
558+
559+
it("should fail authentication using a custom function", async () => {
560+
const { app: appResponse, appResource: app } = await harness.createApp();
561+
562+
const def = await app.functions.create(new StitchFunction(
563+
"funkyAuth",
564+
false,
565+
`
566+
exports = function(payload) {
567+
return 0;
568+
}`
569+
));
570+
571+
await harness.addProvider(app, new CustomFunctionProvider(new CustomFunctionProviderConfig(def.id!!, def.name)));
572+
573+
const client = harness.getAppClient(appResponse);
574+
575+
const id = "123abc";
576+
try {
577+
await client.auth.loginWithCredential(new FunctionCredential({id}));
578+
fail("loginWithCredential with FunctionCredential should have failed");
579+
} catch (error) {
580+
expect(error instanceof StitchServiceError).toBeTruthy();
581+
}
582+
});
583+
523584
it("should call reset password function", async () => {
524585
const { app: appResponse, appResource: app } = await harness.createApp();
525586

packages/core/admin-client/src/authProviders/ProviderConfigs.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,21 @@ class UserpassProvider extends Provider {
6666
}
6767
}
6868

69+
class CustomFunctionProviderConfig {
70+
public constructor(
71+
@jsonProperty("authFunctionId") readonly authFunctionId: string,
72+
@jsonProperty("authFunctionName") readonly authFunctionName: string
73+
) {}
74+
}
75+
76+
class CustomFunctionProvider extends Provider {
77+
public readonly type = "custom-function"
78+
79+
public constructor(public readonly config: CustomFunctionProviderConfig) {
80+
super();
81+
}
82+
}
83+
6984
class CustomProviderConfig {
7085
public constructor(@jsonProperty("signingKey") readonly signingKey: string) {}
7186
}
@@ -85,5 +100,7 @@ export {
85100
UserpassProviderConfig,
86101
UserpassProvider,
87102
CustomProviderConfig,
88-
CustomProvider
103+
CustomProvider,
104+
CustomFunctionProviderConfig,
105+
CustomFunctionProvider
89106
};

packages/core/admin-client/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import { AuthProviderResource, AuthProvidersResource } from "./authProviders/Aut
2121
import {
2222
AnonProviderConfig,
2323
ApiKey,
24+
CustomFunctionProvider,
25+
CustomFunctionProviderConfig,
2426
CustomProvider,
25-
CustomProviderConfig,
27+
CustomProviderConfig,
2628
Provider,
2729
UserpassProvider,
2830
UserpassProviderConfig
@@ -110,6 +112,8 @@ export {
110112
ServicesResource,
111113
CustomProviderConfig,
112114
CustomProvider,
115+
CustomFunctionProvider,
116+
CustomFunctionProviderConfig,
113117
Rule,
114118
RuleResource,
115119
RulesResource,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright 2018-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/** @hidden */
18+
export default class FunctionAuthProvider {
19+
20+
public static TYPE = "custom-function";
21+
public static DEFAULT_NAME = "custom-function";
22+
private constructor() {}
23+
}
24+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Copyright 2018-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import ProviderCapabilities from "../../ProviderCapabilities";
18+
import StitchCredential from "../../StitchCredential";
19+
import FunctionAuthProvider from "./FunctionAuthProvider";
20+
21+
/**
22+
* A credential which can be used to log in as a Stitch user
23+
* using the Function authentication provider.
24+
*/
25+
export default class FunctionCredential implements StitchCredential {
26+
27+
public get providerCapabilities(): ProviderCapabilities {
28+
return new ProviderCapabilities(false);
29+
}
30+
public readonly providerName: string;
31+
public readonly providerType = FunctionAuthProvider.TYPE;
32+
33+
public readonly material: { [key: string]: string };
34+
35+
constructor(
36+
payload: { [key: string]: string },
37+
providerName: string = FunctionAuthProvider.DEFAULT_NAME
38+
) {
39+
this.providerName = providerName;
40+
this.material = payload;
41+
}
42+
}

packages/core/sdk/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import CustomAuthProvider from "./auth/providers/custom/CustomAuthProvider";
4242
import CustomCredential from "./auth/providers/custom/CustomCredential";
4343
import FacebookAuthProvider from "./auth/providers/facebook/FacebookAuthProvider";
4444
import FacebookCredential from "./auth/providers/facebook/FacebookCredential";
45+
import FunctionAuthProvider from "./auth/providers/function/FunctionAuthProvider";
46+
import FunctionCredential from "./auth/providers/function/FunctionCredential";
4547
import GoogleAuthProvider from "./auth/providers/google/GoogleAuthProvider";
4648
import GoogleCredential from "./auth/providers/google/GoogleCredential";
4749
import StitchAuthResponseCredential from "./auth/providers/internal/StitchAuthResponseCredential";
@@ -108,6 +110,8 @@ export {
108110
CustomCredential,
109111
FacebookAuthProvider,
110112
FacebookCredential,
113+
FunctionAuthProvider,
114+
FunctionCredential,
111115
GoogleAuthProvider,
112116
GoogleCredential,
113117
ServerApiKeyAuthProvider,

packages/react-native/coretest/__tests__/StitchAppClientIntTests.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
Anon,
2020
App,
2121
AppResource,
22+
CustomFunctionProvider,
23+
CustomFunctionProviderConfig,
2224
CustomProvider,
2325
CustomProviderConfig,
2426
StitchFunction,
@@ -31,12 +33,15 @@ import {
3133
AnonymousCredential,
3234
CustomAuthProvider,
3335
CustomCredential,
36+
FunctionAuthProvider,
37+
FunctionCredential,
3438
MemoryStorage,
3539
StitchClientError,
3640
StitchClientErrorCode,
3741
UserPasswordAuthProvider,
3842
UserPasswordCredential,
39-
UserType
43+
UserType,
44+
StitchServiceError
4045
} from "mongodb-stitch-core-sdk";
4146
import {
4247
RNAsyncStorage,
@@ -520,6 +525,62 @@ describe("StitchAppClient", () => {
520525
expect(client.auth.listUsers().length).toBe(2);
521526
});
522527

528+
it("should authenticate using a custom function", async () => {
529+
const { app: appResponse, appResource: app } = await harness.createApp();
530+
531+
const def = await app.functions.create(new StitchFunction(
532+
"funkyAuth",
533+
false,
534+
`
535+
exports = function(payload) {
536+
return payload.id;
537+
}`
538+
));
539+
540+
await harness.addProvider(app, new CustomFunctionProvider(new CustomFunctionProviderConfig(def.id!!, def.name)));
541+
542+
const client = await harness.getAppClient(appResponse);
543+
544+
const id = "123abc";
545+
const user = await client.auth.loginWithCredential(new FunctionCredential({id}));
546+
547+
expect(user).toBeDefined();
548+
549+
expect(user.id).toBeDefined();
550+
expect(user.identities[0].id).toBeDefined();
551+
expect(id).toEqual(user.identities[0].id);
552+
expect(FunctionAuthProvider.DEFAULT_NAME).toEqual(user.loggedInProviderName);
553+
expect(FunctionAuthProvider.TYPE).toEqual(user.loggedInProviderType);
554+
expect(UserType.Normal).toEqual(user.userType);
555+
expect(FunctionAuthProvider.TYPE).toEqual(user.identities[0].providerType);
556+
expect(client.auth.isLoggedIn).toBeTruthy();
557+
});
558+
559+
it("should fail authentication using a custom function", async () => {
560+
const { app: appResponse, appResource: app } = await harness.createApp();
561+
562+
const def = await app.functions.create(new StitchFunction(
563+
"funkyAuth",
564+
false,
565+
`
566+
exports = function(payload) {
567+
return 0;
568+
}`
569+
));
570+
571+
await harness.addProvider(app, new CustomFunctionProvider(new CustomFunctionProviderConfig(def.id!!, def.name)));
572+
573+
const client = await harness.getAppClient(appResponse);
574+
575+
const id = "123abc";
576+
try {
577+
await client.auth.loginWithCredential(new FunctionCredential({id}));
578+
fail("loginWithCredential with FunctionCredential should have failed");
579+
} catch (error) {
580+
expect(error instanceof StitchServiceError).toBeTruthy();
581+
}
582+
});
583+
523584
it("should call reset password function", async () => {
524585
const { app: appResponse, appResource: app } = await harness.createApp();
525586

0 commit comments

Comments
 (0)