Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial skeleton and design document for Native Auth features #7414

Draft
wants to merge 11 commits into
base: dev
Choose a base branch
from

Conversation

shenj
Copy link

@shenj shenj commented Nov 11, 2024

No description provided.

@github-actions github-actions bot added documentation Related to documentation. msal-browser Related to msal-browser package labels Nov 11, 2024
lib/msal-native-auth/rollup.config.js Show resolved Hide resolved
lib/msal-native-auth/samples/SignInSample.ts Show resolved Hide resolved
lib/msal-native-auth/package.json Show resolved Hide resolved
@@ -0,0 +1,107 @@
'use strict';
(function (global, factory) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like another sample, do we need this here?

SignUpOptions,
} from "./NativeAuthActionOptions.js";

export interface INativeAuthPublicClientApplication {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion on the naming of the interface (not sure though what is the followed standard):
NativeAuthClient

* Licensed under the MIT License.
*/

export const GrantTypeConstants = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the Constants postfix should not be needed

import { NativeAuthOperatingContext } from "./operating_context/NativeAuthOperatingContext.js";

export class NativeAuthPublicClientApplication
extends PublicClientApplication

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this extends PublicClientApplicatoin from msal-browser?

// Code is required.
// Collect code from customer.
const code = "test-code";
const submitCodeResult = await signInResult.nextStateHandler.submitCode(code);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this not the ideal design, because it starts from the assumption that the signInResults is a a specific type and has a submitCode function. I think as per Single Responsibility, signInResults object should contain only the state of the app.signIn call. The submit function instead should be exposed in the app object, so that is not tight to the result type.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an alternative approach to expose the SDK functions can be like (high level design):

const authApp= NativeAuthPublicClientApplication.create(config);

async function startSignIn() {
  const signInResult = await app.signIn(signInOptions);
  
  if (signInResult .state === SignInState.CodeRequired) {
    navigate("/code-required");
  } else if (signInResult .state === SignInState.PasswordRequired) {
   navigate("/password-required");
  } else if (signInResult .state === SignInState.Error) {
     // show error
  }
}

async function onCodeSubmit(code) {
  const result= await app.signInCodeSubmit(code);
  
  if (signInResult .state === SignInState.CodeRequired) {
    navigate("/code-required");
  } else if (signInResult .state === SignInState.PasswordRequired) {
   navigate("/password-required");
  } else if (signInResult .state === SignInState.Error) {
     // show error
  }else if (signInResult .state === SignInState.Completed) {
     // fetch token
  }
}

By exposing the functions to authApp instance allows decoupling, making more flexible to consume the library.


const accountInfo: AccountInfo | undefined;

if (signInResult.isFlowCompleted()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signInResult should only contain the state of the function call, in this case isFlowCompleted can be converted to a state.

* Creates a new instance of a PublicClientApplication with the given configuration.
* @param config - A configuration object for the PublicClientApplication instance
*/
static create(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a method to retrive as well the created instance

/*
* Reset password handler for the state of code required.
*/
export class ResetPasswordCodeRequiredStateHandler extends ResetPasswordStateHandler {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this class is extending ResetPasswordStateHandler?

Comment on lines 103 to 178
try {
// The authority URL need to be revisited to ensure it is correct.
const authorityUrl = this.config.auth.authority;

// start the signin flow
const signInStartParams: SignInStartParams = new SignInStartParams(
authorityUrl,
this.config.auth.clientId,
correlationId,
this.nativeAuthConfig.nativeAuth.challengeTypes ?? [],
signInOptions.scopes ?? [],
signInOptions.username,
signInOptions.password
);

const startResult = await this.signInClient.start(
signInStartParams
);

if (startResult instanceof SignInWithContinuationTokenResult) {
// require password
if (!signInOptions.password) {
return new SignInResult(
undefined,
new SignInPasswordRequiredStateHandler(
this.signInClient,
correlationId,
startResult.continuationToken,
this.nativeAuthConfig,
signInOptions.scopes
)
);
}

// if the password is provided, then try to get token silently.
const signInSubmitPasswordParams =
new SignInSubmitPasswordParams(
authorityUrl,
this.config.auth.clientId,
correlationId,
this.nativeAuthConfig.nativeAuth.challengeTypes ?? [],
signInOptions.scopes ?? [],
startResult.continuationToken,
signInOptions.password
);

const completedResult = await this.signInClient.submitPassword(
signInSubmitPasswordParams
);

const accountManager = new AccountInfo(
completedResult.authenticationResult.account,
correlationId,
this.nativeAuthConfig
);

return new SignInResult(accountManager);
} else if (startResult instanceof SignInCodeSendResponse) {
// require code
return new SignInResult(
undefined,
new SignInCodeRequiredStateHandler(
this.signInClient,
correlationId,
startResult.continuationToken,
this.nativeAuthConfig,
signInOptions.scopes
)
);
} else {
throw new UnexpectedError("Unknow SignInStartResult type");
}
} catch (error) {
return SignInResult.createWithError(error);
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion here is to refactor this method because there are early returns disconected from each other, making the code hard to read and test.

signInOptions.password
);

const completedResult = await this.signInClient.submitPassword(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The submitPassword method here might not necessarily return a complete, Can the server might respond with another challenge?

* @param signInOptions - Options for signing in the user.
* @returns The result of the operation.
*/
async signIn(signInOptions: SignInInputs): Promise<SignInResult> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed, IMHO, this call should return the signInClient instead of a signInResult, or eventually allow the user to specify the flow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Related to documentation. msal-browser Related to msal-browser package
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants