Skip to content

Conversation

@ryanbas21
Copy link
Contributor

use the prompt=none query parameter.

JIRA Ticket

https://bugster.forgerock.org/jira/browse/SDKS-2060

Description

prompt none provides a better experience when errors occur in an iframe during /authorize

Type of Change

Please Delete options that are not relevant

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

Definition of Done

Check all that apply

  • Acceptance criteria is met.
  • All tasks listed in the user story have been completed.
  • Coded to standards.
  • Code peer-reviewed.
  • Ensure backward compatibility (special attention).
  • API reference docs is updated.
  • Unit tests are written.
  • Integration tests are written.
  • e2e tests are written.
  • CI build passing on the feature branch.
  • Functional spec is written/updated
  • contains example code snippets.
  • Change log updated.
  • Documentation story is created and tracked.
  • UI is completed or ticket is created.
  • Demo to PO and team.
  • Tech debts and remaining tasks are tracked in separated ticket(s).

Documentation

  • Acceptance criteria met
  • Spell-check run
  • Peer reviewed
  • Proofread

Comment on lines 165 to 170
const error_description_query_param =
new URL(response.url).searchParams.get('error_description') !== undefined;

if (error_description_query_param) {
throw new Error(allowedErrors.PromptError);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

honestly unsure if this was really a great way to handle this. need to look into this more

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is needed. The parseError method used just a few lines down should already grab the error and throw with the right message.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2023

@nx-cloud
Copy link

nx-cloud bot commented Feb 9, 2023

☁️ Nx Cloud Report

CI is running/has finished running commands for commit f9e2622. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this branch


✅ Successfully ran 5 targets

Sent with 💌 from NxCloud.

Copy link
Contributor

@cerebrl cerebrl left a comment

Choose a reason for hiding this comment

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

Just a few comments.

CORSError: 'Cross-origin redirection',

//Prompt error
PromptError: 'User is not authenticated or session cookie not sent to the server',
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the error parameter may be better than the error_description parameter. Rather than this long sentence, you'd look for this: interaction_required.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, looking at this now, the most minimal code change would be to use both the error and error_description as defined by this parseError method: https://github.com/ForgeRock/forgerock-javascript-sdk/blob/develop/packages/javascript-sdk/src/oauth2-client/index.ts#L293.

Comment on lines 165 to 170
const error_description_query_param =
new URL(response.url).searchParams.get('error_description') !== undefined;

if (error_description_query_param) {
throw new Error(allowedErrors.PromptError);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is needed. The parseError method used just a few lines down should already grab the error and throw with the right message.

* Options used when requesting the authorization URL.
*/
interface GetAuthorizationUrlOptions extends ConfigOptions {
interface GetAuthorizationUrlOptions extends GetTokensOptions {
Copy link
Contributor

Choose a reason for hiding this comment

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

Was this intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was because of the above cahnge of passing login, i dont think its needed now, however its more correct from the type perspective i think.

Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't feel right to me as the hierarchy of this is in reverse. The TokenManager class, on which getTokens is found, internally uses the OAuthClient class. So, if one inherited from the other, I would think GetTokensOptions would extend GetAuthorizationUrlOptions, not the other way around, yeah? But, ultimately, I don't think either extend the other. For example, forceRenew and login are not properties of GetAuthorizationUrlOptions as the createAuthorizeUrl doesn't use them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we resolved this on slack. its right and wrong and i've created a ticket to complete a fix for this.

@ryanbas21 ryanbas21 temporarily deployed to Preview February 13, 2023 20:39 — with GitHub Actions Inactive
@ryanbas21 ryanbas21 temporarily deployed to Preview February 13, 2023 20:47 — with GitHub Actions Inactive
@github-actions github-actions bot added the root label Feb 13, 2023
use the prompt=none query parameter.
@ryanbas21 ryanbas21 temporarily deployed to Preview February 15, 2023 14:52 — with GitHub Actions Inactive
@github-actions github-actions bot removed the test label Feb 15, 2023
@ryanbas21 ryanbas21 temporarily deployed to Preview February 15, 2023 15:32 — with GitHub Actions Inactive
@ryanbas21 ryanbas21 marked this pull request as ready for review February 15, 2023 17:41
state?: string;
verifier?: string;
query?: StringDict<string>;
prompt?: 'none' | 'redirect' | 'consent';
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you meant "login", rather than "redirect", yeah? Ref: https://backstage.forgerock.com/docs/am/7/oauth2-guide/oauth2-authorize-endpoint.html

Copy link
Contributor Author

Choose a reason for hiding this comment

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

d'oh. yes.

* Options used when requesting the authorization URL.
*/
interface GetAuthorizationUrlOptions extends ConfigOptions {
interface GetAuthorizationUrlOptions extends GetTokensOptions {
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't feel right to me as the hierarchy of this is in reverse. The TokenManager class, on which getTokens is found, internally uses the OAuthClient class. So, if one inherited from the other, I would think GetTokensOptions would extend GetAuthorizationUrlOptions, not the other way around, yeah? But, ultimately, I don't think either extend the other. For example, forceRenew and login are not properties of GetAuthorizationUrlOptions as the createAuthorizeUrl doesn't use them.

CORSError: 'Cross-origin redirection',

// prompt=none errors
InteractionNotAllowed: 'The request requires some interaction that is not allowed.',
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit confused here. Being that the parseError method concatenates the error and error_description, I would think this string would start with interaction_required: : https://github.com/ForgeRock/forgerock-javascript-sdk/blob/develop/packages/javascript-sdk/src/oauth2-client/index.ts#L296. Am I misunderstanding what happens here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Screen Shot 2023-02-15 at 2 52 49 PM

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, okay, you're right. Sorry for the confusion.

@ryanbas21 ryanbas21 temporarily deployed to Preview February 15, 2023 22:10 — with GitHub Actions Inactive
const authorizeUrlOptions = { ...options, responseType: ResponseType.Code, state, verifier };
const authorizeUrl = await OAuth2Client.createAuthorizeUrl(authorizeUrlOptions);

const authorizeUrlOptions = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this got formatted /shrug

// Since `login` is configured for "redirect", store authorize values and redirect
window.sessionStorage.setItem(clientId as string, JSON.stringify(authorizeUrlOptions));

const authorizeUrl = await OAuth2Client.createAuthorizeUrl(authorizeUrlOptions);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

i moved this from above, not needed but its closer to where its used now

BREAKING CHANGE: commit 48cc494  didn't get picked up for breaking
change, unsure why so i'm adding it here. there isn o breaking change in
this commit
@ryanbas21 ryanbas21 temporarily deployed to Preview February 15, 2023 22:27 — with GitHub Actions Inactive
@github-actions github-actions bot removed the samples label Feb 15, 2023
@ryanbas21 ryanbas21 merged commit 36585ed into develop Feb 16, 2023
Copy link
Contributor

@daveadams56 daveadams56 left a comment

Choose a reason for hiding this comment

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

I see this was merged before I hit 'submit' on my review so apologies for not getting feedback to you before this, however I have a couple of questions around the implementation. Could both be things that I missed, but it would be good to discuss these here or in slack when you get time. Thanks.

state?: string;
verifier?: string;
query?: StringDict<string>;
prompt?: 'none' | 'login' | 'consent';
Copy link
Contributor

Choose a reason for hiding this comment

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

My understanding is that the requirement in the ticket was for none to mean that ?prompt=none is passed in the authorize call, login and would be used for centralized login. Maybe a silly question, but what does consent mean here - what is the consequence of specifying this option?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a good point. These three values for the /authorize endpoint are valid, but we are likely unable to support the 'consent' option. Should remove this option as to not confuse the dev?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is misleading if it doesn't actually do anything or isn't compatible in the context of the SDK and should be removed. I'd worry that it might inadvertently trick developers into thinking some action should occur if they specify consent, for example showing a consent collection page.

*/
public static async getAuthCodeByIframe(options: GetAuthorizationUrlOptions): Promise<string> {
const url = await this.createAuthorizeUrl(options);
const url = await this.createAuthorizeUrl({ ...options, prompt: 'none' });
Copy link
Contributor

Choose a reason for hiding this comment

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

Given that we are setting prompt=none as a default, does it make sense to make this something the developer can specify? As I understand it, setting prompt: 'login' overrides this but if nothing is set then it's prompt: 'none'. Maybe I'm overthinking this but what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good question. This is how I look at it: since this is within the hidden iframe, I don't believe there is ever a reason to NOT use prompt=none as any prompt, aka UI, would be hidden and therefore unusable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. Given this and my point about the value consent further up, perhaps we should rethink this. Maybe we should obscure this entirely from the user and always pass prompt=none unless the developer specifies they want to do centralized login using login: 'redirect'? That way we avoid introducing a "thing" that the developer has to think about.

@cerebrl cerebrl deleted the prompt-none branch June 12, 2023 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

4 participants