From 255644875cf9c76baf834edfedfad604bea14895 Mon Sep 17 00:00:00 2001 From: Bogdan Gavril Date: Wed, 9 Aug 2023 15:40:12 +0100 Subject: [PATCH 01/16] Move changelog to root --- msal4j-sdk/changelog.txt => changelog.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename msal4j-sdk/changelog.txt => changelog.txt (100%) diff --git a/msal4j-sdk/changelog.txt b/changelog.txt similarity index 100% rename from msal4j-sdk/changelog.txt rename to changelog.txt From ddc953f78ba61ae447250764bf83047789a43122 Mon Sep 17 00:00:00 2001 From: Bogdan Gavril Date: Tue, 12 Sep 2023 17:29:18 +0100 Subject: [PATCH 02/16] Update issue templates (#707) --- .github/ISSUE_TEMPLATE/bug_report.md | 125 +++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..d788b3f9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,125 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +name: Bug report +description: Broken or unintended behavior with one of our libraries. +title: '[Bug] ' +labels: ["untriaged", "needs attention"] +body: +- type: markdown + attributes: + value: | + ## Before submitting your issue + Please make sure that your question or issue is not already covered in existing issues + + **Logs and network traces** + Without logs or traces, it is unlikely that the team can investigate your issue. Capturing logs is described in our [Docs](https://learn.microsoft.com/azure/active-directory/develop/msal-logging-java). + +- type: markdown + attributes: + value: | + ## Issue details + +- type: input + attributes: + label: Library version used + description: "Please enter the version of the library where you ran into the issue (e.g. 1.13.10)." + validations: + required: true + +- type: input + attributes: + label: Java version + description: "Please enter the Java SDK and Framework version your application is developed in." + validations: + required: true + +- type: dropdown + attributes: + label: Scenario + description: "Are you using PublicClientApplication, ConfidentialClientApplication or ManagedIdentityApplication?" + multiple: true + options: + - "PublicClient - desktop app" + - "PublicClient - mobile app" + - "ConfidentialClient - web site (AcquireTokenByAuthCode)" + - "ConfidentialClient - web api (AcquireTokenOnBehalfOf)" + - "ConfidentialClient - service to service (AcquireTokenForClient)" + - "ManagedIdentityClient - managed identity" + - "Other - please specify" + validations: + required: true + +- type: dropdown + attributes: + label: Is this a new or an existing app? + description: "Is this a new or existing app?" + multiple: false + options: + - "The app is in production, and I have upgraded to a new version of MSAL" + - "The app is in production, I haven't upgraded MSAL, but started seeing this issue" + - "This is a new app or experiment" + validations: + required: false + +- type: textarea + attributes: + label: Issue description and reproduction steps + description: "Briefly explain the issue you are seeing along with any error messages or stack trace. Provide a link to one of the [standard samples](https://learn.microsoft.com/azure/active-directory/develop/sample-v2-code?tabs=apptype) and steps to reproduce the behavior. Make sure to provide verbose level log messages from MSAL, if available. [Learn more](https://learn.microsoft.com/azure/active-directory/develop/msal-logging-dotnet)" + validations: + required: true + +- type: textarea + attributes: + label: Relevant code snippets + description: "Provide relevant code snippets that can be used to reproduce the issue." + render: csharp + validations: + required: false + +- type: textarea + attributes: + label: Expected behavior + description: "Describe what you expect the behavior to be." + validations: + required: false + +- type: dropdown + attributes: + label: Identity provider + options: + - Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts) + - Azure B2C Basic Policy + - Azure B2C Custom Policy + - Azure Active Directory Federation Services (ADFS) + - Microsoft Entra External ID + - Other + validations: + required: true + +- type: input + attributes: + label: Regression + description: "If this behavior worked before, enter the last working version(s) of MSAL." + placeholder: "MSAL version: " + +- type: textarea + attributes: + label: Solution and workarounds + description: "Possible solution or workarounds, if you know of any." + validations: + required: false + +- type: markdown + attributes: + value: "## Security Reporting" +- type: markdown + attributes: + value: | + If you find a security issue with our libraries or services [please report it to the Microsoft Security Response Center (MSRC)](https://aka.ms/report-security-issue) with as much detail as possible. Your submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting [this page](https://www.microsoft.com/msrc/technical-security-notifications) and subscribing to Security Advisory Alerts. From afc8a15a133230c7e771320b9af06a599183ac53 Mon Sep 17 00:00:00 2001 From: Avery-Dunn <62066438+Avery-Dunn@users.noreply.github.com> Date: Thu, 21 Sep 2023 09:28:53 -0700 Subject: [PATCH 03/16] Re-add lombok source line (#705) --- msal4j-sdk/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/msal4j-sdk/pom.xml b/msal4j-sdk/pom.xml index ef8e12b5..bd92dd15 100644 --- a/msal4j-sdk/pom.xml +++ b/msal4j-sdk/pom.xml @@ -169,6 +169,7 @@ + ${project.build.directory}/delombok org.projectlombok From f31cd8b50a987a144d9dbb85e1e29d6a37e3edd3 Mon Sep 17 00:00:00 2001 From: Avery-Dunn <62066438+Avery-Dunn@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:44:24 -0700 Subject: [PATCH 04/16] Version changes for release 1.13.11 (#714) --- README.md | 6 +++--- changelog.txt | 4 ++++ msal4j-sdk/README.md | 6 +++--- msal4j-sdk/bnd.bnd | 2 +- msal4j-sdk/pom.xml | 2 +- msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml | 2 +- msal4j-sdk/src/samples/msal-obo-sample/pom.xml | 2 +- msal4j-sdk/src/samples/msal-web-sample/pom.xml | 2 +- 8 files changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5e99abf0..0ddb3d98 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Quick links: The library supports the following Java environments: - Java 8 (or higher) -Current version - 1.13.10 +Current version - 1.13.11 You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/main/msal4j-sdk/changelog.txt). @@ -28,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti com.microsoft.azure msal4j - 1.13.10 + 1.13.11 ``` ### Gradle ```gradle -implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.13.10' +implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.13.11' ``` ## Usage diff --git a/changelog.txt b/changelog.txt index 380e95a4..24639066 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +Version 1.13.11 +============= +- Hotfix for internal docs generation issue (#705) + Version 1.13.10 ============= - Remove default HTTP timeout (#664) diff --git a/msal4j-sdk/README.md b/msal4j-sdk/README.md index dce0869b..474499bc 100644 --- a/msal4j-sdk/README.md +++ b/msal4j-sdk/README.md @@ -16,7 +16,7 @@ Quick links: The library supports the following Java environments: - Java 8 (or higher) -Current version - 1.13.10 +Current version - 1.13.11 You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/master/changelog.txt). @@ -28,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti com.microsoft.azure msal4j - 1.13.10 + 1.13.11 ``` ### Gradle ```gradle -compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.13.10' +compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.13.11' ``` ## Usage diff --git a/msal4j-sdk/bnd.bnd b/msal4j-sdk/bnd.bnd index d3b2aa1a..0a14dff8 100644 --- a/msal4j-sdk/bnd.bnd +++ b/msal4j-sdk/bnd.bnd @@ -1,2 +1,2 @@ -Export-Package: com.microsoft.aad.msal4j;version="1.13.10" +Export-Package: com.microsoft.aad.msal4j;version="1.13.11" Automatic-Module-Name: com.microsoft.aad.msal4j diff --git a/msal4j-sdk/pom.xml b/msal4j-sdk/pom.xml index bd92dd15..847b32e7 100644 --- a/msal4j-sdk/pom.xml +++ b/msal4j-sdk/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.microsoft.azure msal4j - 1.13.10 + 1.13.11 jar msal4j diff --git a/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml b/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml index 775c4e14..416d4d36 100644 --- a/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.13.10 + 1.13.11 com.nimbusds diff --git a/msal4j-sdk/src/samples/msal-obo-sample/pom.xml b/msal4j-sdk/src/samples/msal-obo-sample/pom.xml index 86615a41..860ebce8 100644 --- a/msal4j-sdk/src/samples/msal-obo-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-obo-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.13.10 + 1.13.11 com.nimbusds diff --git a/msal4j-sdk/src/samples/msal-web-sample/pom.xml b/msal4j-sdk/src/samples/msal-web-sample/pom.xml index 2d06caed..bb6760fb 100644 --- a/msal4j-sdk/src/samples/msal-web-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.13.10 + 1.13.11 com.nimbusds From 746631ef43cd01c9774e65525dfd2a857d4eb629 Mon Sep 17 00:00:00 2001 From: Bogdan Gavril Date: Fri, 29 Sep 2023 11:45:31 +0100 Subject: [PATCH 05/16] Update bug report --- .github/ISSUE_TEMPLATE/bug_report.yaml | 125 +++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 00000000..d788b3f9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,125 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +name: Bug report +description: Broken or unintended behavior with one of our libraries. +title: '[Bug] ' +labels: ["untriaged", "needs attention"] +body: +- type: markdown + attributes: + value: | + ## Before submitting your issue + Please make sure that your question or issue is not already covered in existing issues + + **Logs and network traces** + Without logs or traces, it is unlikely that the team can investigate your issue. Capturing logs is described in our [Docs](https://learn.microsoft.com/azure/active-directory/develop/msal-logging-java). + +- type: markdown + attributes: + value: | + ## Issue details + +- type: input + attributes: + label: Library version used + description: "Please enter the version of the library where you ran into the issue (e.g. 1.13.10)." + validations: + required: true + +- type: input + attributes: + label: Java version + description: "Please enter the Java SDK and Framework version your application is developed in." + validations: + required: true + +- type: dropdown + attributes: + label: Scenario + description: "Are you using PublicClientApplication, ConfidentialClientApplication or ManagedIdentityApplication?" + multiple: true + options: + - "PublicClient - desktop app" + - "PublicClient - mobile app" + - "ConfidentialClient - web site (AcquireTokenByAuthCode)" + - "ConfidentialClient - web api (AcquireTokenOnBehalfOf)" + - "ConfidentialClient - service to service (AcquireTokenForClient)" + - "ManagedIdentityClient - managed identity" + - "Other - please specify" + validations: + required: true + +- type: dropdown + attributes: + label: Is this a new or an existing app? + description: "Is this a new or existing app?" + multiple: false + options: + - "The app is in production, and I have upgraded to a new version of MSAL" + - "The app is in production, I haven't upgraded MSAL, but started seeing this issue" + - "This is a new app or experiment" + validations: + required: false + +- type: textarea + attributes: + label: Issue description and reproduction steps + description: "Briefly explain the issue you are seeing along with any error messages or stack trace. Provide a link to one of the [standard samples](https://learn.microsoft.com/azure/active-directory/develop/sample-v2-code?tabs=apptype) and steps to reproduce the behavior. Make sure to provide verbose level log messages from MSAL, if available. [Learn more](https://learn.microsoft.com/azure/active-directory/develop/msal-logging-dotnet)" + validations: + required: true + +- type: textarea + attributes: + label: Relevant code snippets + description: "Provide relevant code snippets that can be used to reproduce the issue." + render: csharp + validations: + required: false + +- type: textarea + attributes: + label: Expected behavior + description: "Describe what you expect the behavior to be." + validations: + required: false + +- type: dropdown + attributes: + label: Identity provider + options: + - Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts) + - Azure B2C Basic Policy + - Azure B2C Custom Policy + - Azure Active Directory Federation Services (ADFS) + - Microsoft Entra External ID + - Other + validations: + required: true + +- type: input + attributes: + label: Regression + description: "If this behavior worked before, enter the last working version(s) of MSAL." + placeholder: "MSAL version: " + +- type: textarea + attributes: + label: Solution and workarounds + description: "Possible solution or workarounds, if you know of any." + validations: + required: false + +- type: markdown + attributes: + value: "## Security Reporting" +- type: markdown + attributes: + value: | + If you find a security issue with our libraries or services [please report it to the Microsoft Security Response Center (MSRC)](https://aka.ms/report-security-issue) with as much detail as possible. Your submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting [this page](https://www.microsoft.com/msrc/technical-security-notifications) and subscribing to Security Advisory Alerts. From 681328689e48cbdbbefeb4cf40c9db0a3d9e2ae7 Mon Sep 17 00:00:00 2001 From: Bogdan Gavril Date: Fri, 29 Sep 2023 11:48:56 +0100 Subject: [PATCH 06/16] Delete .github/ISSUE_TEMPLATE/bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 125 --------------------------- 1 file changed, 125 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index d788b3f9..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -name: Bug report -description: Broken or unintended behavior with one of our libraries. -title: '[Bug] ' -labels: ["untriaged", "needs attention"] -body: -- type: markdown - attributes: - value: | - ## Before submitting your issue - Please make sure that your question or issue is not already covered in existing issues - - **Logs and network traces** - Without logs or traces, it is unlikely that the team can investigate your issue. Capturing logs is described in our [Docs](https://learn.microsoft.com/azure/active-directory/develop/msal-logging-java). - -- type: markdown - attributes: - value: | - ## Issue details - -- type: input - attributes: - label: Library version used - description: "Please enter the version of the library where you ran into the issue (e.g. 1.13.10)." - validations: - required: true - -- type: input - attributes: - label: Java version - description: "Please enter the Java SDK and Framework version your application is developed in." - validations: - required: true - -- type: dropdown - attributes: - label: Scenario - description: "Are you using PublicClientApplication, ConfidentialClientApplication or ManagedIdentityApplication?" - multiple: true - options: - - "PublicClient - desktop app" - - "PublicClient - mobile app" - - "ConfidentialClient - web site (AcquireTokenByAuthCode)" - - "ConfidentialClient - web api (AcquireTokenOnBehalfOf)" - - "ConfidentialClient - service to service (AcquireTokenForClient)" - - "ManagedIdentityClient - managed identity" - - "Other - please specify" - validations: - required: true - -- type: dropdown - attributes: - label: Is this a new or an existing app? - description: "Is this a new or existing app?" - multiple: false - options: - - "The app is in production, and I have upgraded to a new version of MSAL" - - "The app is in production, I haven't upgraded MSAL, but started seeing this issue" - - "This is a new app or experiment" - validations: - required: false - -- type: textarea - attributes: - label: Issue description and reproduction steps - description: "Briefly explain the issue you are seeing along with any error messages or stack trace. Provide a link to one of the [standard samples](https://learn.microsoft.com/azure/active-directory/develop/sample-v2-code?tabs=apptype) and steps to reproduce the behavior. Make sure to provide verbose level log messages from MSAL, if available. [Learn more](https://learn.microsoft.com/azure/active-directory/develop/msal-logging-dotnet)" - validations: - required: true - -- type: textarea - attributes: - label: Relevant code snippets - description: "Provide relevant code snippets that can be used to reproduce the issue." - render: csharp - validations: - required: false - -- type: textarea - attributes: - label: Expected behavior - description: "Describe what you expect the behavior to be." - validations: - required: false - -- type: dropdown - attributes: - label: Identity provider - options: - - Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts) - - Azure B2C Basic Policy - - Azure B2C Custom Policy - - Azure Active Directory Federation Services (ADFS) - - Microsoft Entra External ID - - Other - validations: - required: true - -- type: input - attributes: - label: Regression - description: "If this behavior worked before, enter the last working version(s) of MSAL." - placeholder: "MSAL version: " - -- type: textarea - attributes: - label: Solution and workarounds - description: "Possible solution or workarounds, if you know of any." - validations: - required: false - -- type: markdown - attributes: - value: "## Security Reporting" -- type: markdown - attributes: - value: | - If you find a security issue with our libraries or services [please report it to the Microsoft Security Response Center (MSRC)](https://aka.ms/report-security-issue) with as much detail as possible. Your submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting [this page](https://www.microsoft.com/msrc/technical-security-notifications) and subscribing to Security Advisory Alerts. From 041bd0117d125415077a119783eb9e2a7354bb84 Mon Sep 17 00:00:00 2001 From: Bogdan Gavril Date: Fri, 29 Sep 2023 11:52:42 +0100 Subject: [PATCH 07/16] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index d788b3f9..439b5ef8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -1,14 +1,5 @@ ---- name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -name: Bug report -description: Broken or unintended behavior with one of our libraries. +description: Broken or unintended behavior with MSAL4J library title: '[Bug] ' labels: ["untriaged", "needs attention"] body: @@ -19,7 +10,7 @@ body: Please make sure that your question or issue is not already covered in existing issues **Logs and network traces** - Without logs or traces, it is unlikely that the team can investigate your issue. Capturing logs is described in our [Docs](https://learn.microsoft.com/azure/active-directory/develop/msal-logging-java). + Without logs or traces, it is unlikely that the team can investigate your issue. Capturing logs is described in our [Docs](https://learn.microsoft.com/azure/active-directory/develop/msal-logging-java). - type: markdown attributes: @@ -29,14 +20,14 @@ body: - type: input attributes: label: Library version used - description: "Please enter the version of the library where you ran into the issue (e.g. 1.13.10)." + description: "Enter the version of the library where you ran into the issue (e.g. 1.13.10)." validations: required: true - type: input attributes: label: Java version - description: "Please enter the Java SDK and Framework version your application is developed in." + description: "Enter the Java SDK and Framework version your application is developed in." validations: required: true @@ -46,8 +37,7 @@ body: description: "Are you using PublicClientApplication, ConfidentialClientApplication or ManagedIdentityApplication?" multiple: true options: - - "PublicClient - desktop app" - - "PublicClient - mobile app" + - "PublicClient (AcquireTokenInteractive, AcquireTokenByUsernamePassword)" - "ConfidentialClient - web site (AcquireTokenByAuthCode)" - "ConfidentialClient - web api (AcquireTokenOnBehalfOf)" - "ConfidentialClient - service to service (AcquireTokenForClient)" From 09c82224a500a55179ce72afa7ac97f59183658e Mon Sep 17 00:00:00 2001 From: Bogdan Gavril Date: Fri, 29 Sep 2023 11:55:01 +0100 Subject: [PATCH 08/16] Create FeatureRequest.yaml --- .github/ISSUE_TEMPLATE/FeatureRequest.yaml | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/FeatureRequest.yaml diff --git a/.github/ISSUE_TEMPLATE/FeatureRequest.yaml b/.github/ISSUE_TEMPLATE/FeatureRequest.yaml new file mode 100644 index 00000000..3ecb4027 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FeatureRequest.yaml @@ -0,0 +1,48 @@ +name: Feature request +description: Suggest a new feature for MSAL.NET. +labels: ["feature request", "untriaged", "needs attention"] +title : '[Feature Request] ' +body: +- type: markdown + attributes: + value: | + ## Before submitting your feature request + Please make sure that your question or issue is not already covered in [MSAL documentation](https://learn.microsoft.com/entra/msal/dotnet/) or [samples](https://learn.microsoft.com/azure/active-directory/develop/sample-v2-code?tabs=apptype). + +- type: markdown + attributes: + value: | + ## Feature request for MSAL.NET + +- type: dropdown + attributes: + label: MSAL client type + description: Are you using PublicClientApplication (desktop and mobile apps), ConfidentialClientApplication (web apps, web APIs, service-to-service) or ManagedIdentityApplication? + multiple: true + options: + - "Public" + - "Confidential" + - "Managed identity" + validations: + required: true + +- type: textarea + attributes: + label: Problem Statement + description: "Describe the problem or context for this feature request." + validations: + required: true + +- type: textarea + attributes: + label: Proposed solution + description: "Describe the solution you'd like." + validations: + required: false + +- type: textarea + attributes: + label: Alternatives + description: "Describe alternatives you've considered." + validations: + required: false From 2edc49bc392d64ce388649a4688d52f2ca52b946 Mon Sep 17 00:00:00 2001 From: Bogdan Gavril Date: Fri, 29 Sep 2023 11:56:36 +0100 Subject: [PATCH 09/16] Update FeatureRequest.yaml --- .github/ISSUE_TEMPLATE/FeatureRequest.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/FeatureRequest.yaml b/.github/ISSUE_TEMPLATE/FeatureRequest.yaml index 3ecb4027..a112882e 100644 --- a/.github/ISSUE_TEMPLATE/FeatureRequest.yaml +++ b/.github/ISSUE_TEMPLATE/FeatureRequest.yaml @@ -1,5 +1,5 @@ name: Feature request -description: Suggest a new feature for MSAL.NET. +description: Suggest a new feature for MSAL Java labels: ["feature request", "untriaged", "needs attention"] title : '[Feature Request] ' body: @@ -7,17 +7,17 @@ body: attributes: value: | ## Before submitting your feature request - Please make sure that your question or issue is not already covered in [MSAL documentation](https://learn.microsoft.com/entra/msal/dotnet/) or [samples](https://learn.microsoft.com/azure/active-directory/develop/sample-v2-code?tabs=apptype). + Please make sure that your question or issue is not already covered in [MSAL documentation](https://learn.microsoft.com/entra/msal/java/) or [samples](https://learn.microsoft.com/azure/active-directory/develop/sample-v2-code?tabs=apptype). - type: markdown attributes: value: | - ## Feature request for MSAL.NET + ## Feature request for MSAL Java - type: dropdown attributes: label: MSAL client type - description: Are you using PublicClientApplication (desktop and mobile apps), ConfidentialClientApplication (web apps, web APIs, service-to-service) or ManagedIdentityApplication? + description: Are you using PublicClientApplication (desktop / CLI apps), ConfidentialClientApplication (web apps, web APIs, service-to-service) or ManagedIdentityApplication? multiple: true options: - "Public" From 39eb7d8f8a4394165b919187af984daac67cbcf7 Mon Sep 17 00:00:00 2001 From: akulyakhtin Date: Fri, 6 Oct 2023 20:36:35 +0300 Subject: [PATCH 10/16] Set default throttling time to 5 sec (#721) Co-authored-by: Kulyakhtin, Alexander (Ext) --- .../src/main/java/com/microsoft/aad/msal4j/ThrottlingCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/ThrottlingCache.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/ThrottlingCache.java index 89e1f348..9c8a0584 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/ThrottlingCache.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/ThrottlingCache.java @@ -12,7 +12,7 @@ class ThrottlingCache { static final int MAX_THROTTLING_TIME_SEC = 3600; - static int DEFAULT_THROTTLING_TIME_SEC = 120; + static int DEFAULT_THROTTLING_TIME_SEC = 5; static final int CACHE_SIZE_LIMIT_TO_TRIGGER_EXPIRED_ENTITIES_REMOVAL = 100; // request hash to expiration timestamp From 8f00ea0db7b9ac72d0c333b9e03f97ef79247b42 Mon Sep 17 00:00:00 2001 From: Avery-Dunn <62066438+Avery-Dunn@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:35:33 -0700 Subject: [PATCH 11/16] MSAL Java/MSALRuntime integration (#590) * Add IBroker implementation for MSALRuntime * Remove dll used during testing * Integrate broker steps to relevant flows in PublicClientApplication * Add logic to cancel MsalRuntimeFutures * Expand javadocs and exception handling * Address code review comments * Simplify future chaining, address code review comments * Reorganize future chaining, fix testing issues * Adjust how broker availability is checked * Create automated test * Adjust startup logic * Correct version number for interop * Correct broker versioning * Move broker tests to MSAL Java package * Remove usage of msal4j-brokers from msal4j * Add missing SLFJ dependency * Use newest msal4j * Bump javamsalruntime version number * Version changes for 1.14.0-beta release (#589) * Add missing pom info needed by sonatype * APIs for toggling MSALRuntime's logging (#608) * Add APIs for toggling MSALRuntime's logging systems * Rename logging methods to be more clear * Add support for POP tokens to MSAL Java and MSAL Java Brokers (#639) * Version changes for 1.14.0-beta release * regional endpoint change to always use login.microsoft.com * Add support for both current and legacy B2C authority formats (#594) * Add support for both current and legacy B2C authority formats * Fix B2C format test * add 2 seconds timeout while calling IMDS * Fix failing tests * Fix failing tests * delete commented out code * Use the dedicated admin consent endpoint instead of a query parameter (#599) * updated versions for release * update condition to throw exception * added test for invalid authority * Add tests for a CIAM user and reduce test code duplication (#603) * Add tests for a CIAM user and reduce code duplication in several test files * Revert changed method name * Attempt to resolve credscan flag * Resolve credscan issues * Address code review comments * Use default scope * expose extraQueryParameters * expose extraQueryParameters * ExtraQueryParameters tests * retrigger the tests * Updated an existing test case to check added parameters * Replace exception with warning * version updates for release * update json-smart version * Updated json-smart version Updated json-smart version to a 'bug-free' version * version updates for release * Initial commit * add CIAM authority file * revert authority validation changes * Fix failing tests * Fix failing tests * remove commented out line * remove unnecessary code * update exception message for device code flow * add refresh_in logic * resolve build issues + address PR comments * update tests * updated org-json version to resolve Dependabot alert * Better redirect URI error handling and dependency upgrade (#633) * Better error handling for redirect URIs * Update oauth2-oidc-sdk dependency * Address review comments Co-authored-by: Bogdan Gavril --------- Co-authored-by: Bogdan Gavril * Version updates for 1.13.8 release (#634) * Version updates for 1.13.8 release * Update changelog.txt * Point to MSAL Java reference docs onboarded to Microsoft Learn * Add support for POP tokens to MSAL Java and MSAL Java Brokers * Send extraQueryParameters to interop's AuthParameters * Avoid exposing new PopParameters class, change API to match design doc * Update msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java Co-authored-by: Bogdan Gavril * Update msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java Co-authored-by: Bogdan Gavril * Update change log URl in README.md (#649) Replaced broken link in change log reference with msal4j-sdk/changelog.txt * Issue 447 * Feedback incorporation * enum for os type * Use enum for HTTP methods * Add broker tests, address PR review comments * Improve PoP tests * Address code review comments * Version updates * Re-add extraQueryParameters support --------- Co-authored-by: siddhijain Co-authored-by: Bogdan Gavril Co-authored-by: Dickson Mwendia <64727760+Dickson-Mwendia@users.noreply.github.com> Co-authored-by: Tamas Csizmadia * Fix silent issue * Ensure correlation ID is never null * Broker fixes and feedback (#733) * Delete codeql.yml * Test framework update (#672) * Initial working tests * Remove CIAM extra query parameter * Fix failing tests * Remove duplicate unit tests * Remove duplicate unit tests * Update tests with mocking to use Mockito * Remove testng and powermock, add junit and mockito * Remove AbstractMsalTests and PowerMockTestCase * Fix mistaken null check * Properly scope dependency * Update CIAM tests (#673) * Bump guava from 31.1-jre to 32.0.0-jre in /msal4j-sdk (#671) Bumps [guava](https://github.com/google/guava) from 31.1-jre to 32.0.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Avery-Dunn * Delete contributing.md (#667) Co-authored-by: Avery-Dunn * Create Contributing.md (#668) Co-authored-by: Avery-Dunn * Version changes for 1.13.9 (#674) * Add space between command and arguments when executing linux command to open browser. Refs #682 (#683) Co-authored-by: Ric Emery * Assorted fixes (#684) * Remove default timeouts and improve exception messages * Fix NPE for on-prem ADFS scenario * Log MSAL message but re-throw exception * Update vulnerable test dependency * Issue-679: Fix for Account Cache; .contains() was not possible and you had to iterate through all elements as workaround. (#681) * Version changes for 1.13.10 (#685) * Move changelog * Move changelog to root * Update issue templates (#707) * Re-add lombok source line (#705) * Version changes for release 1.13.11 (#714) * Update bug report * Delete .github/ISSUE_TEMPLATE/bug_report.md * Update bug_report.yaml * Create FeatureRequest.yaml * Update FeatureRequest.yaml * Set default throttling time to 5 sec (#721) Co-authored-by: Kulyakhtin, Alexander (Ext) * Ensure correlation ID is never null * Rename MsalRuntimeBroker and add builder pattern for better API consistency --------- Signed-off-by: dependabot[bot] Co-authored-by: Bogdan Gavril Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ric Emery Co-authored-by: Ric Emery Co-authored-by: Maximilian Pfeffer Co-authored-by: akulyakhtin Co-authored-by: Kulyakhtin, Alexander (Ext) * Version changes for msal4j-brokers 1.0.3-beta and msal4j 1.14.3-beta (#734) * Ensure correlation ID is never null * Version changes for msal4j-brokers 1.0.3-beta and msal4j 1.14.3-beta * Ensure that builder values for supported OS's are used * Release 1.14.0/1.0.0 version changes (#736) * Delete codeql.yml * Test framework update (#672) * Initial working tests * Remove CIAM extra query parameter * Fix failing tests * Remove duplicate unit tests * Remove duplicate unit tests * Update tests with mocking to use Mockito * Remove testng and powermock, add junit and mockito * Remove AbstractMsalTests and PowerMockTestCase * Fix mistaken null check * Properly scope dependency * Update CIAM tests (#673) * Bump guava from 31.1-jre to 32.0.0-jre in /msal4j-sdk (#671) Bumps [guava](https://github.com/google/guava) from 31.1-jre to 32.0.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Avery-Dunn * Delete contributing.md (#667) Co-authored-by: Avery-Dunn * Create Contributing.md (#668) Co-authored-by: Avery-Dunn * Version changes for 1.13.9 (#674) * Add space between command and arguments when executing linux command to open browser. Refs #682 (#683) Co-authored-by: Ric Emery * Assorted fixes (#684) * Remove default timeouts and improve exception messages * Fix NPE for on-prem ADFS scenario * Log MSAL message but re-throw exception * Update vulnerable test dependency * Issue-679: Fix for Account Cache; .contains() was not possible and you had to iterate through all elements as workaround. (#681) * Version changes for 1.13.10 (#685) * Move changelog * Move changelog to root * Update issue templates (#707) * Re-add lombok source line (#705) * Version changes for release 1.13.11 (#714) * Update bug report * Delete .github/ISSUE_TEMPLATE/bug_report.md * Update bug_report.yaml * Create FeatureRequest.yaml * Update FeatureRequest.yaml * Set default throttling time to 5 sec (#721) Co-authored-by: Kulyakhtin, Alexander (Ext) * Version changes for 1.14.0 msal4j and 1.0.0 msal4j-brokers --------- Signed-off-by: dependabot[bot] Co-authored-by: Bogdan Gavril Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ric Emery Co-authored-by: Ric Emery Co-authored-by: Maximilian Pfeffer Co-authored-by: akulyakhtin Co-authored-by: Kulyakhtin, Alexander (Ext) --------- Signed-off-by: dependabot[bot] Co-authored-by: siddhijain Co-authored-by: Bogdan Gavril Co-authored-by: Dickson Mwendia <64727760+Dickson-Mwendia@users.noreply.github.com> Co-authored-by: Tamas Csizmadia Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ric Emery Co-authored-by: Ric Emery Co-authored-by: Maximilian Pfeffer Co-authored-by: akulyakhtin Co-authored-by: Kulyakhtin, Alexander (Ext) --- changelog.txt | 17 +- msal4j-brokers/changelog.txt | 4 + msal4j-brokers/pom.xml | 73 ++++- .../microsoft/aad/msal4jbrokers/Broker.java | 280 ++++++++++++++++++ .../aad/msal4jbrokers/MSALRuntimeBroker.java | 31 -- .../infrastructure/SeleniumConstants.java | 21 ++ .../infrastructure/SeleniumExtensions.java | 115 +++++++ .../test/java/labapi/HttpClientHelper.java | 62 ++++ .../java/labapi/KeyVaultSecretsProvider.java | 112 +++++++ .../src/test/java/labapi/LabConstants.java | 24 ++ .../src/test/java/labapi/LabService.java | 99 +++++++ .../src/test/java/labapi/LabUserProvider.java | 46 +++ msal4j-brokers/src/test/java/labapi/User.java | 36 +++ .../test/java/labapi/UserQueryParameters.java | 13 + .../src/test/java/labapi/UserSecret.java | 14 + .../test/java/test/ProofOfPossessionTest.java | 184 ++++++++++++ msal4j-sdk/README.md | 7 +- msal4j-sdk/bnd.bnd | 2 +- msal4j-sdk/pom.xml | 2 +- .../aad/msal4j/AuthenticationErrorCode.java | 11 + .../aad/msal4j/AuthenticationResult.java | 3 + .../com/microsoft/aad/msal4j/HttpMethod.java | 42 ++- .../com/microsoft/aad/msal4j/IBroker.java | 79 +++-- .../msal4j/InteractiveRequestParameters.java | 33 +++ .../microsoft/aad/msal4j/PopParameters.java | 44 +++ .../aad/msal4j/PublicClientApplication.java | 128 +++++++- .../aad/msal4j/SilentParameters.java | 22 ++ .../msal4j/UserNamePasswordParameters.java | 18 ++ 28 files changed, 1443 insertions(+), 79 deletions(-) create mode 100644 msal4j-brokers/changelog.txt create mode 100644 msal4j-brokers/src/main/java/com/microsoft/aad/msal4jbrokers/Broker.java delete mode 100644 msal4j-brokers/src/main/java/com/microsoft/aad/msal4jbrokers/MSALRuntimeBroker.java create mode 100644 msal4j-brokers/src/test/java/infrastructure/SeleniumConstants.java create mode 100644 msal4j-brokers/src/test/java/infrastructure/SeleniumExtensions.java create mode 100644 msal4j-brokers/src/test/java/labapi/HttpClientHelper.java create mode 100644 msal4j-brokers/src/test/java/labapi/KeyVaultSecretsProvider.java create mode 100644 msal4j-brokers/src/test/java/labapi/LabConstants.java create mode 100644 msal4j-brokers/src/test/java/labapi/LabService.java create mode 100644 msal4j-brokers/src/test/java/labapi/LabUserProvider.java create mode 100644 msal4j-brokers/src/test/java/labapi/User.java create mode 100644 msal4j-brokers/src/test/java/labapi/UserQueryParameters.java create mode 100644 msal4j-brokers/src/test/java/labapi/UserSecret.java create mode 100644 msal4j-brokers/src/test/java/test/ProofOfPossessionTest.java create mode 100644 msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/PopParameters.java diff --git a/changelog.txt b/changelog.txt index 24639066..81d5fd17 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,19 @@ -Version 1.13.11 +Version 1.14.0 ============= -- Hotfix for internal docs generation issue (#705) +- GA release of MSAL Java Brokers package +- Add support for acquiring bearer and proof-of-possession tokens using WAM as the broker (#590) +- Default throttling time for password grant requests lowered to 5 seconds (#721) +- Fix internal docs generation issue (#705) + +Version 1.14.1-beta +============= +- Add proof-of-possession token support +- Add MSALRuntime logging support + +Version 1.14.0-beta +============= +- Add IBroker interface +- Add app-level parameter for enabling the use of auth brokers Version 1.13.10 ============= diff --git a/msal4j-brokers/changelog.txt b/msal4j-brokers/changelog.txt new file mode 100644 index 00000000..ff5f398a --- /dev/null +++ b/msal4j-brokers/changelog.txt @@ -0,0 +1,4 @@ +Version 1.0.0 +============= +- Initial release +- Provides the API and dependencies needed to acquire tokens via WAM \ No newline at end of file diff --git a/msal4j-brokers/pom.xml b/msal4j-brokers/pom.xml index 060d756e..646e23a7 100644 --- a/msal4j-brokers/pom.xml +++ b/msal4j-brokers/pom.xml @@ -5,12 +5,11 @@ 4.0.0 com.microsoft.azure msal4j-brokers - 0.0.1 + 1.0.0 jar msal4j-brokers - Microsoft Authentication Library for Java - Brokers helps you integrate with the broker - on windows machine to secure Access tokens and refresh tokens. + Microsoft Authentication Library for Java - Brokers is a companion package for MSAL Java that allows easy integration with authentication brokers https://github.com/AzureAD/microsoft-authentication-library-for-java @@ -22,15 +21,25 @@ https://github.com/AzureAD/microsoft-authentication-library-for-java + + + ms + Microsoft Corporation + + UTF-8 - com.microsoft.azure msal4j - 1.13.2 + 1.14.0 + + + com.microsoft.azure + javamsalruntime + 0.13.10 org.projectlombok @@ -38,6 +47,59 @@ 1.18.6 provided + + org.testng + testng + 7.1.0 + test + + + org.slf4j + slf4j-api + 1.7.36 + + + ch.qos.logback + logback-classic + 1.2.3 + test + + + commons-io + commons-io + 2.11.0 + test + + + org.seleniumhq.selenium + selenium-api + 3.14.0 + test + + + org.seleniumhq.selenium + selenium-chrome-driver + 3.14.0 + test + + + org.seleniumhq.selenium + selenium-support + 3.14.0 + test + + + com.azure + azure-core + 1.22.0 + test + + + com.azure + azure-security-keyvault-secrets + 4.3.5 + test + @@ -60,7 +122,6 @@ - ${project.build.directory}/delombok org.projectlombok diff --git a/msal4j-brokers/src/main/java/com/microsoft/aad/msal4jbrokers/Broker.java b/msal4j-brokers/src/main/java/com/microsoft/aad/msal4jbrokers/Broker.java new file mode 100644 index 00000000..da56b08f --- /dev/null +++ b/msal4j-brokers/src/main/java/com/microsoft/aad/msal4jbrokers/Broker.java @@ -0,0 +1,280 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4jbrokers; + +import com.microsoft.aad.msal4j.*; +import com.microsoft.azure.javamsalruntime.Account; +import com.microsoft.azure.javamsalruntime.AuthParameters; +import com.microsoft.azure.javamsalruntime.AuthResult; +import com.microsoft.azure.javamsalruntime.MsalInteropException; +import com.microsoft.azure.javamsalruntime.MsalRuntimeInterop; +import com.microsoft.azure.javamsalruntime.ReadAccountResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class Broker implements IBroker { + private static final Logger LOG = LoggerFactory.getLogger(Broker.class); + + private static MsalRuntimeInterop interop; + private static Boolean brokerAvailable; + + private boolean supportWindows; + + static { + try { + //MsalRuntimeInterop performs various initialization steps in a similar static block, + // so when an MsalRuntimeBroker is created this will cause the interop layer to initialize + interop = new MsalRuntimeInterop(); + } catch (MsalInteropException e) { + throw new MsalClientException(String.format("Could not initialize MSALRuntime: %s", e.getErrorMessage()), AuthenticationErrorCode.MSALRUNTIME_INTEROP_ERROR); + } + } + + @Override + public CompletableFuture acquireToken(PublicClientApplication application, SilentParameters parameters) { + String correlationID = application.correlationId() == null ? generateCorrelationID() : application.correlationId(); + Account accountResult = null; + + //If request has an account ID, MSALRuntime likely has data cached for that account that we can retrieve + if (parameters.account() != null) { + try { + accountResult = ((ReadAccountResult) interop.readAccountById(parameters.account().homeAccountId().split("\\.")[0], correlationID).get()).getAccount(); + } catch (InterruptedException | ExecutionException ex) { + throw new MsalClientException(String.format("MSALRuntime async operation interrupted when waiting for result: %s", ex.getMessage()), AuthenticationErrorCode.MSALRUNTIME_INTEROP_ERROR); + } + } + + try { + AuthParameters.AuthParametersBuilder authParamsBuilder = new AuthParameters. + AuthParametersBuilder(application.clientId(), + application.authority(), + String.join(" ", parameters.scopes())) + .additionalParameters(parameters.extraQueryParameters()); + + //If POP auth scheme configured, set parameters to get MSALRuntime to return POP tokens + if (parameters.proofOfPossession() != null) { + authParamsBuilder.popParameters(parameters.proofOfPossession().getHttpMethod().methodName, + parameters.proofOfPossession().getUri(), + parameters.proofOfPossession().getNonce()); + } + + AuthParameters authParameters = authParamsBuilder.build(); + + if (accountResult == null) { + return interop.signInSilently(authParameters, correlationID) + .thenCompose(acctResult -> interop.acquireTokenSilently(authParameters, correlationID, ((AuthResult) acctResult).getAccount())) + .thenApply(authResult -> parseBrokerAuthResult( + application.authority(), + ((AuthResult) authResult).getIdToken(), + ((AuthResult) authResult).getAccessToken(), + ((AuthResult) authResult).getAccount().getAccountId(), + ((AuthResult) authResult).getAccount().getClientInfo(), + ((AuthResult) authResult).getAccessTokenExpirationTime(), + ((AuthResult) authResult).isPopAuthorization())); + } else { + return interop.acquireTokenSilently(authParameters, correlationID, accountResult) + .thenApply(authResult -> parseBrokerAuthResult(application.authority(), + ((AuthResult) authResult).getIdToken(), + ((AuthResult) authResult).getAccessToken(), + ((AuthResult) authResult).getAccount().getAccountId(), + ((AuthResult) authResult).getAccount().getClientInfo(), + ((AuthResult) authResult).getAccessTokenExpirationTime(), + ((AuthResult) authResult).isPopAuthorization())); + } + } catch (MsalInteropException interopException) { + throw new MsalClientException(interopException.getErrorMessage(), AuthenticationErrorCode.MSALRUNTIME_INTEROP_ERROR); + } + } + + @Override + public CompletableFuture acquireToken(PublicClientApplication application, InteractiveRequestParameters parameters) { + String correlationID = application.correlationId() == null ? generateCorrelationID() : application.correlationId(); + + try { + AuthParameters.AuthParametersBuilder authParamsBuilder = new AuthParameters. + AuthParametersBuilder(application.clientId(), + application.authority(), + String.join(" ", parameters.scopes())) + .redirectUri(parameters.redirectUri().toString()) + .additionalParameters(parameters.extraQueryParameters()); + + //If POP auth scheme configured, set parameters to get MSALRuntime to return POP tokens + if (parameters.proofOfPossession() != null) { + authParamsBuilder.popParameters(parameters.proofOfPossession().getHttpMethod().methodName, + parameters.proofOfPossession().getUri(), + parameters.proofOfPossession().getNonce()); + } + + AuthParameters authParameters = authParamsBuilder.build(); + + return interop.signInInteractively(parameters.windowHandle(), authParameters, correlationID, parameters.loginHint()) + .thenCompose(acctResult -> interop.acquireTokenInteractively(parameters.windowHandle(), authParameters, correlationID, ((AuthResult) acctResult).getAccount())) + .thenApply(authResult -> parseBrokerAuthResult( + application.authority(), + ((AuthResult) authResult).getIdToken(), + ((AuthResult) authResult).getAccessToken(), + ((AuthResult) authResult).getAccount().getAccountId(), + ((AuthResult) authResult).getAccount().getClientInfo(), + ((AuthResult) authResult).getAccessTokenExpirationTime(), + ((AuthResult) authResult).isPopAuthorization())); + } catch (MsalInteropException interopException) { + throw new MsalClientException(interopException.getErrorMessage(), AuthenticationErrorCode.MSALRUNTIME_INTEROP_ERROR); + } + } + + /** + * @deprecated + */ + @Deprecated + @Override + public CompletableFuture acquireToken(PublicClientApplication application, UserNamePasswordParameters parameters) { + String correlationID = application.correlationId() == null ? generateCorrelationID() : application.correlationId(); + + try { + AuthParameters.AuthParametersBuilder authParamsBuilder = new AuthParameters. + AuthParametersBuilder(application.clientId(), + application.authority(), + String.join(" ", parameters.scopes())) + .additionalParameters(parameters.extraQueryParameters()); + + //If POP auth scheme configured, set parameters to get MSALRuntime to return POP tokens + if (parameters.proofOfPossession() != null) { + authParamsBuilder.popParameters(parameters.proofOfPossession().getHttpMethod().methodName, + parameters.proofOfPossession().getUri(), + parameters.proofOfPossession().getNonce()); + } + + AuthParameters authParameters = authParamsBuilder.build(); + + return interop.signInSilently(authParameters, correlationID) + .thenCompose(acctResult -> interop.acquireTokenSilently(authParameters, correlationID, ((AuthResult) acctResult).getAccount())) + .thenApply(authResult -> parseBrokerAuthResult( + application.authority(), + ((AuthResult) authResult).getIdToken(), + ((AuthResult) authResult).getAccessToken(), + ((AuthResult) authResult).getAccount().getAccountId(), + ((AuthResult) authResult).getAccount().getClientInfo(), + ((AuthResult) authResult).getAccessTokenExpirationTime(), + ((AuthResult) authResult).isPopAuthorization())); + } catch (MsalInteropException interopException) { + throw new MsalClientException(interopException.getErrorMessage(), AuthenticationErrorCode.MSALRUNTIME_INTEROP_ERROR); + } + } + + @Override + public void removeAccount(PublicClientApplication application, IAccount msalJavaAccount) { + String correlationID = application.correlationId() == null ? generateCorrelationID() : application.correlationId(); + + try { + Account msalRuntimeAccount = ((ReadAccountResult) interop.readAccountById(msalJavaAccount.homeAccountId().split("\\.")[0], correlationID).get()).getAccount(); + + if (msalRuntimeAccount != null) { + interop.signOutSilently(application.clientId(), correlationID, msalRuntimeAccount); + } + } catch (MsalInteropException interopException) { + throw new MsalClientException(interopException.getErrorMessage(), AuthenticationErrorCode.MSALRUNTIME_INTEROP_ERROR); + } catch (InterruptedException | ExecutionException ex) { + throw new MsalClientException(String.format("MSALRuntime async operation interrupted when waiting for result: %s", ex.getMessage()), AuthenticationErrorCode.MSALRUNTIME_INTEROP_ERROR); + } + } + + /** + * Calls MSALRuntime's startup API. If MSALRuntime started successfully, we can assume that the broker is available for use. + * + * If an exception is thrown when trying to start MSALRuntime, we assume that we cannot use the broker and will not make any more attempts to do so. + * + * @return boolean representing whether or not MSALRuntime started successfully + */ + @Override + public boolean isBrokerAvailable() { + //brokerAvailable is only set after the first attempt to call MSALRuntime's startup API + if (brokerAvailable == null) { + try { + interop.startupMsalRuntime(); + + LOG.info("MSALRuntime started successfully. MSAL Java will use MSALRuntime in all supported broker flows."); + + brokerAvailable = true; + } catch (MsalInteropException e) { + LOG.warn("Exception thrown when trying to start MSALRuntime: {}", e.getErrorMessage()); + LOG.warn("MSALRuntime could not be started. MSAL Java will fall back to non-broker flows."); + + brokerAvailable = false; + } + } + + return brokerAvailable; + } + + /** + * Toggles whether or not detailed MSALRuntime logs will appear in MSAL Java's normal logging framework. + * + * If enabled, you will see logs directly from MSALRuntime, containing verbose information relating to telemetry, API calls,successful/failed requests, and more. + * These logs will appear alongside MSAL Java's logs (with a message indicating they came from MSALRuntime), and will follow the same log level as MSAL Java's logs (info/debug/error/etc.). + * + * If disabled (default), MSAL Java will still produce some logs related to MSALRuntime, particularly in error messages, but will be much less verbose. + * + * @param enableLogging true to enable MSALRuntime logs, false to disable it + */ + public void enableBrokerLogging(boolean enableLogging) { + try { + MsalRuntimeInterop.enableLogging(enableLogging); + } catch (Exception ex) { + throw new MsalClientException(String.format("Error occurred when calling MSALRuntime logging API: %s", ex.getMessage()), AuthenticationErrorCode.MSALRUNTIME_INTEROP_ERROR); + } + } + + /** + * If enabled, Personal Identifiable Information (PII) can appear in logs and error messages produced by MSALRuntime. + * + * If disabled (default), PII will not be shown, and you will simply see "(PII)" or similar notes in places where PII data would have appeared. + * + * @param enablePII true to allow PII to appear in logs and error messages, false to disallow it + */ + public void enableBrokerPIILogging(boolean enablePII) { + try { + MsalRuntimeInterop.enableLoggingPii(enablePII); + } catch (Exception ex) { + throw new MsalClientException(String.format("Error occurred when calling MSALRuntime PII logging API: %s", ex.getMessage()), AuthenticationErrorCode.MSALRUNTIME_INTEROP_ERROR); + } + } + + //Generates a random correlation ID, used when a correlation ID was not set at the application level + private String generateCorrelationID() { + return UUID.randomUUID().toString(); + } + + public static class Builder { + private boolean supportWindows = false; + + public Builder() { + } + + /** + * When set to true, MSAL Java will attempt to use the broker when the application is running on a Windows OS + */ + public Builder supportWindows(boolean val) { + supportWindows = val; + return this; + } + + public Broker build() { + return new Broker(this); + } + } + + private Broker(Builder builder) { + this.supportWindows = builder.supportWindows; + + //This will be expanded to cover other OS options, but for now it is only Windows. Since Windows is the only + // option, if app developer doesn't want to use the broker on Windows then they shouldn't use the Broker at all + if (!this.supportWindows) { + throw new MsalClientException("At least one operating system support option must be used when building the Broker instance", AuthenticationErrorCode.MSALJAVA_BROKERS_ERROR); + } + } +} diff --git a/msal4j-brokers/src/main/java/com/microsoft/aad/msal4jbrokers/MSALRuntimeBroker.java b/msal4j-brokers/src/main/java/com/microsoft/aad/msal4jbrokers/MSALRuntimeBroker.java deleted file mode 100644 index 598b83ac..00000000 --- a/msal4j-brokers/src/main/java/com/microsoft/aad/msal4jbrokers/MSALRuntimeBroker.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.microsoft.aad.msal4jbrokers; - -import com.microsoft.aad.msal4j.*; -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.CompletableFuture; - -@Slf4j -public class MSALRuntimeBroker implements IBroker { - - @Override - public IAuthenticationResult acquireToken(PublicClientApplication application, SilentParameters requestParameters) { - log.debug("Should not call this API if msal runtime init failed"); - throw new MsalClientException("Broker implementation missing", "missing_broker"); - } - - @Override - public IAuthenticationResult acquireToken(PublicClientApplication application, InteractiveRequestParameters requestParameters) { - throw new MsalClientException("Broker implementation missing", "missing_broker"); - } - - @Override - public IAuthenticationResult acquireToken(PublicClientApplication application, UserNamePasswordParameters requestParameters) { - throw new MsalClientException("Broker implementation missing", "missing_broker"); - } - - @Override - public CompletableFuture removeAccount(IAccount account) { - throw new MsalClientException("Broker implementation missing", "missing_broker"); - } -} diff --git a/msal4j-brokers/src/test/java/infrastructure/SeleniumConstants.java b/msal4j-brokers/src/test/java/infrastructure/SeleniumConstants.java new file mode 100644 index 00000000..859a9bd8 --- /dev/null +++ b/msal4j-brokers/src/test/java/infrastructure/SeleniumConstants.java @@ -0,0 +1,21 @@ +//---------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ + +package infrastructure; + +public class SeleniumConstants { + final static String WEB_UPN_INPUT_ID = "i0116"; + final static String WEB_PASSWORD_ID = "i0118"; + final static String WEB_SUBMIT_ID = "idSIButton9"; + + // Stay signed in? + final static String STAY_SIGN_IN_NO_BUTTON_ID = "idBtn_Back"; + + // Are you trying to sign in to ... + //Only continue if you downloaded the app from a store or website that you trust. + final static String ARE_YOU_TRYING_TO_SIGN_IN_TO = "idSIButton9"; +} diff --git a/msal4j-brokers/src/test/java/infrastructure/SeleniumExtensions.java b/msal4j-brokers/src/test/java/infrastructure/SeleniumExtensions.java new file mode 100644 index 00000000..bf46d23e --- /dev/null +++ b/msal4j-brokers/src/test/java/infrastructure/SeleniumExtensions.java @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package infrastructure; + +import labapi.User; +import org.openqa.selenium.By; +import org.openqa.selenium.StaleElementReferenceException; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.TimeUnit; + +public class SeleniumExtensions { + + private final static Logger LOG = LoggerFactory.getLogger(SeleniumExtensions.class); + + private SeleniumExtensions() { + } + + public static WebDriver createDefaultWebDriver() { + ChromeOptions options = new ChromeOptions(); + //no visual rendering, remove when debugging + options.addArguments("--headless"); + + System.setProperty("webdriver.chrome.driver", "C:/Windows/chromedriver.exe"); + ChromeDriver driver = new ChromeDriver(options); + driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); + + return driver; + } + + public static WebElement waitForElementToBeVisibleAndEnable(WebDriver driver, By by, int timeOutInSeconds) { + WebDriverWait webDriverWait = new WebDriverWait(driver, timeOutInSeconds); + return webDriverWait.until((dr) -> + { + try { + WebElement elementToBeDisplayed = driver.findElement(by); + if (elementToBeDisplayed.isDisplayed() && elementToBeDisplayed.isEnabled()) { + return elementToBeDisplayed; + } + return null; + } catch (StaleElementReferenceException e) { + return null; + } + }); + } + + public static WebElement waitForElementToBeVisibleAndEnable(WebDriver driver, By by) { + int DEFAULT_TIMEOUT_IN_SEC = 15; + + return waitForElementToBeVisibleAndEnable(driver, by, DEFAULT_TIMEOUT_IN_SEC); + } + + public static void performADLogin(WebDriver driver, User user) { + LOG.info("PerformADLogin"); + + LOG.info("Loggin in ... Entering username"); + driver.findElement(new By.ById(SeleniumConstants.WEB_UPN_INPUT_ID)).sendKeys(user.getUpn()); + + LOG.info("Loggin in ... Clicking after username"); + driver.findElement(new By.ById(SeleniumConstants.WEB_SUBMIT_ID)).click(); + + LOG.info("Loggin in ... Entering password"); + By by = new By.ById(SeleniumConstants.WEB_PASSWORD_ID); + waitForElementToBeVisibleAndEnable(driver, by).sendKeys(user.getPassword()); + + LOG.info("Loggin in ... click submit"); + waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.WEB_SUBMIT_ID)). + click(); + + try { + checkAuthenticationCompletePage(driver); + return; + } catch (TimeoutException ex) { + } + + LOG.info("Checking optional questions"); + + try { + LOG.info("Are you trying to sign in to ... ? checking"); + waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.ARE_YOU_TRYING_TO_SIGN_IN_TO), 3). + click(); + LOG.info("Are you trying to sign in to ... ? click Continue"); + + } catch (TimeoutException ex) { + } + + try { + LOG.info("Stay signed in? checking"); + waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.STAY_SIGN_IN_NO_BUTTON_ID), 3). + click(); + LOG.info("Stay signed in? click NO"); + } catch (TimeoutException ex) { + } + } + + private static void checkAuthenticationCompletePage(WebDriver driver) { + (new WebDriverWait(driver, 5)).until((ExpectedCondition) d -> { + boolean condition = false; + WebElement we = d.findElement(new By.ByTagName("body")); + if (we != null && we.getText().contains("Authentication complete")) { + condition = true; + } + return condition; + }); + } +} diff --git a/msal4j-brokers/src/test/java/labapi/HttpClientHelper.java b/msal4j-brokers/src/test/java/labapi/HttpClientHelper.java new file mode 100644 index 00000000..e4d2eb1e --- /dev/null +++ b/msal4j-brokers/src/test/java/labapi/HttpClientHelper.java @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package labapi; + +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.util.Map; + +class HttpClientHelper { + static String sendRequestToLab(String url, Map queryMap, String accessToken) + throws IOException { + return sendRequestToLab(buildUrl(url, queryMap), accessToken); + } + + static String sendRequestToLab(URL labUrl, String accessToken) throws IOException { + HttpsURLConnection conn = (HttpsURLConnection)labUrl.openConnection(); + + conn.setRequestProperty("Authorization", "Bearer " + accessToken); + + conn.setReadTimeout(20000); + conn.setConnectTimeout(20000); + + StringBuilder content; + try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + String inputLine; + content = new StringBuilder(); + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + } + conn.disconnect(); + return content.toString(); + } + + private static URL buildUrl(String url, Map queryMap) + throws MalformedURLException, UnsupportedOperationException { + String queryParameters; + queryParameters = queryMap.entrySet() + .stream() + .map(p -> encodeUTF8(p.getKey()) + "=" + encodeUTF8(p.getValue())) + .reduce((p1, p2) -> p1 + "&" + p2) + .orElse(""); + + String urlString = url + "?" + queryParameters; + return new URL(urlString); + } + + private static String encodeUTF8(String s) { + try { + return URLEncoder.encode(s, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Error: cannot encode query parameter " + s); + } + } +} diff --git a/msal4j-brokers/src/test/java/labapi/KeyVaultSecretsProvider.java b/msal4j-brokers/src/test/java/labapi/KeyVaultSecretsProvider.java new file mode 100644 index 00000000..673b550a --- /dev/null +++ b/msal4j-brokers/src/test/java/labapi/KeyVaultSecretsProvider.java @@ -0,0 +1,112 @@ +package labapi; + +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenCredential; +import com.azure.security.keyvault.secrets.SecretClient; +import com.azure.security.keyvault.secrets.SecretClientBuilder; +import com.azure.security.keyvault.secrets.models.KeyVaultSecretIdentifier; +import com.microsoft.aad.msal4j.ClientCredentialFactory; +import com.microsoft.aad.msal4j.ClientCredentialParameters; +import com.microsoft.aad.msal4j.ConfidentialClientApplication; +import com.microsoft.aad.msal4j.IAuthenticationResult; +import com.microsoft.aad.msal4j.IClientCredential; +import reactor.core.publisher.Mono; + +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class KeyVaultSecretsProvider { + private final SecretClient secretClient; + + private static final String CLIENT_ID = "2afb0add-2f32-4946-ac90-81a02aa4550e"; + public static String CERTIFICATE_ALIAS = "MsalJavaAutomationRunner"; + + private static final String WIN_KEYSTORE = "Windows-MY"; + private static final String KEYSTORE_PROVIDER = "SunMSCAPI"; + + private static final String MAC_KEYSTORE = "KeychainStore"; + + static Map cache = new ConcurrentHashMap<>(); + + KeyVaultSecretsProvider() { + secretClient = getAuthenticatedSecretClient(); + } + + String getSecret(String secretUrl) { + // extract keyName from secretUrl + KeyVaultSecretIdentifier keyVaultSecretIdentifier = new KeyVaultSecretIdentifier(secretUrl); + String key = keyVaultSecretIdentifier.getName(); + + if (cache.containsKey(key)) { + return cache.get(key); + } + + String secret = secretClient.getSecret(key).getValue(); + cache.put(key, secret); + + return secret; + } + + private SecretClient getAuthenticatedSecretClient() { + return new SecretClientBuilder() + .credential(getTokenCredential()) + .vaultUrl(LabConstants.MSIDLAB_VAULT_URL) + .buildClient(); + } + + private AccessToken requestAccessTokenForAutomation() { + IAuthenticationResult result; + try { + ConfidentialClientApplication cca = + ConfidentialClientApplication + .builder(CLIENT_ID, getClientCredentialFromKeyStore()) + .authority(LabConstants.MICROSOFT_AUTHORITY) + .build(); + result = cca.acquireToken(ClientCredentialParameters + .builder(Collections.singleton( + LabConstants.KEYVAULT_DEFAULT_SCOPE)) + .build()) + .get(); + } catch (Exception e) { + throw new RuntimeException("Error acquiring token from Azure AD: " + e.getMessage()); + } + if (result != null) { + return new AccessToken( + result.accessToken(), + OffsetDateTime.ofInstant(result.expiresOnDate().toInstant(), ZoneOffset.UTC)); + } else { + throw new NullPointerException("Authentication result is null"); + } + } + + private IClientCredential getClientCredentialFromKeyStore() { + PrivateKey key; + X509Certificate publicCertificate; + try { + String os = System.getProperty("os.name"); + KeyStore keystore; + if (os.toLowerCase().contains("windows")) { + keystore = KeyStore.getInstance(WIN_KEYSTORE, KEYSTORE_PROVIDER); + } else { + keystore = KeyStore.getInstance(MAC_KEYSTORE); + } + + keystore.load(null, null); + key = (PrivateKey)keystore.getKey(CERTIFICATE_ALIAS, null); + publicCertificate = (X509Certificate)keystore.getCertificate(CERTIFICATE_ALIAS); + } catch (Exception e) { + throw new RuntimeException("Error getting certificate from keystore: " + e.getMessage()); + } + return ClientCredentialFactory.createFromCertificate(key, publicCertificate); + } + + private TokenCredential getTokenCredential() { + return tokenRequestContext -> Mono.defer(() -> Mono.just(requestAccessTokenForAutomation())); + } +} diff --git a/msal4j-brokers/src/test/java/labapi/LabConstants.java b/msal4j-brokers/src/test/java/labapi/LabConstants.java new file mode 100644 index 00000000..37e4f921 --- /dev/null +++ b/msal4j-brokers/src/test/java/labapi/LabConstants.java @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package labapi; + +public class LabConstants { + public final static String KEYVAULT_DEFAULT_SCOPE = "https://vault.azure.net/.default"; + public final static String MSIDLAB_DEFAULT_SCOPE = "https://msidlab.com/.default"; + public final static String MSIDLAB_VAULT_URL = "https://msidlabs.vault.azure.net/"; + + public final static String MICROSOFT_AUTHORITY = + "https://login.microsoftonline.com/microsoft.onmicrosoft.com"; + + public final static String LAB_USER_ENDPOINT = "https://msidlab.com/api/user"; + public final static String LAB_USER_SECRET_ENDPOINT = "https://msidlab.com/api/LabSecret"; + + public final static String APP_ID_KEY_VAULT_SECRET = + "https://msidlabs.vault.azure.net/secrets/LabVaultAppID"; + public final static String APP_PASSWORD_KEY_VAULT_SECRET = + "https://msidlabs.vault.azure.net/secrets/LabVaultAppSecret"; + + public final static String AZURE_ENVIRONMENT = "azurecloud"; + public final static String FEDERATION_PROVIDER_NONE = "none"; +} diff --git a/msal4j-brokers/src/test/java/labapi/LabService.java b/msal4j-brokers/src/test/java/labapi/LabService.java new file mode 100644 index 00000000..f15690bc --- /dev/null +++ b/msal4j-brokers/src/test/java/labapi/LabService.java @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package labapi; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.microsoft.aad.msal4j.ClientCredentialFactory; +import com.microsoft.aad.msal4j.ClientCredentialParameters; +import com.microsoft.aad.msal4j.ConfidentialClientApplication; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +public class LabService { + static ConfidentialClientApplication labApp; + + static ObjectMapper mapper = + new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + static T convertJsonToObject(final String json, final Class clazz) { + try { + return mapper.readValue(json, clazz); + } catch (IOException e) { + throw new RuntimeException("JSON processing error: " + e.getMessage(), e); + } + } + + static void initLabApp() throws MalformedURLException { + KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); + + String appID = keyVaultSecretsProvider.getSecret(LabConstants.APP_ID_KEY_VAULT_SECRET); + String appSecret = + keyVaultSecretsProvider.getSecret(LabConstants.APP_PASSWORD_KEY_VAULT_SECRET); + + labApp = ConfidentialClientApplication + .builder(appID, ClientCredentialFactory.createFromSecret(appSecret)) + .authority(LabConstants.MICROSOFT_AUTHORITY) + .build(); + } + + static String getLabAccessToken() + throws MalformedURLException, ExecutionException, InterruptedException { + if (labApp == null) { + initLabApp(); + } + return labApp + .acquireToken( + ClientCredentialParameters + .builder(Collections.singleton(LabConstants.MSIDLAB_DEFAULT_SCOPE)) + .build()) + .get() + .accessToken(); + } + + User getUser(UserQueryParameters query) { + try { + Map queryMap = query.parameters; + String result = HttpClientHelper.sendRequestToLab( + LabConstants.LAB_USER_ENDPOINT, queryMap, getLabAccessToken()); + + User[] users = convertJsonToObject(result, User[].class); + User user = users[0]; + if (user.getUserType().equals("Guest")) { + String secretId = user.getHomeDomain().split("\\.")[0]; + user.setPassword(getSecret(secretId)); + } else { + user.setPassword(getSecret(user.getLabName())); + } + if (query.parameters.containsKey(UserQueryParameters.FEDERATION_PROVIDER)) { + user.setFederationProvider( + query.parameters.get(UserQueryParameters.FEDERATION_PROVIDER)); + } else { + user.setFederationProvider(LabConstants.FEDERATION_PROVIDER_NONE); + } + return user; + } catch (Exception ex) { + throw new RuntimeException("Error getting user from lab: " + ex.getMessage()); + } + } + + public static String getSecret(String labName) { + String result; + try { + Map queryMap = new HashMap<>(); + queryMap.put("secret", labName); + result = HttpClientHelper.sendRequestToLab( + LabConstants.LAB_USER_SECRET_ENDPOINT, queryMap, getLabAccessToken()); + + return convertJsonToObject(result, UserSecret.class).value; + } catch (Exception ex) { + throw new RuntimeException("Error getting user secret from lab: " + ex.getMessage()); + } + } +} diff --git a/msal4j-brokers/src/test/java/labapi/LabUserProvider.java b/msal4j-brokers/src/test/java/labapi/LabUserProvider.java new file mode 100644 index 00000000..cc37543e --- /dev/null +++ b/msal4j-brokers/src/test/java/labapi/LabUserProvider.java @@ -0,0 +1,46 @@ +//---------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ + +package labapi; + +import java.util.HashMap; +import java.util.Map; + +public class LabUserProvider { + private static LabUserProvider instance; + + private final LabService labService; + private Map userCache; + + private LabUserProvider() { + labService = new LabService(); + userCache = new HashMap<>(); + } + + public static synchronized LabUserProvider getInstance() { + if (instance == null) { + instance = new LabUserProvider(); + } + return instance; + } + + public User getDefaultUser() { + UserQueryParameters query = new UserQueryParameters(); + query.parameters.put(UserQueryParameters.AZURE_ENVIRONMENT, LabConstants.AZURE_ENVIRONMENT); + + return getLabUser(query); + } + + public User getLabUser(UserQueryParameters userQuery) { + if (userCache.containsKey(userQuery)) { + return userCache.get(userQuery); + } + User response = labService.getUser(userQuery); + userCache.put(userQuery, response); + return response; + } +} diff --git a/msal4j-brokers/src/test/java/labapi/User.java b/msal4j-brokers/src/test/java/labapi/User.java new file mode 100644 index 00000000..64584380 --- /dev/null +++ b/msal4j-brokers/src/test/java/labapi/User.java @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package labapi; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; + +@Getter +public class User { + @JsonProperty("appId") + private String appId; + + @JsonProperty("userType") + private String userType; + + @JsonProperty("upn") + @Setter + private String upn; + + @JsonProperty("homeDomain") + private String homeDomain; + + @JsonProperty("homeUPN") + private String homeUPN; + + @JsonProperty("labName") + private String labName; + + @Setter + private String password; + + @Setter + private String federationProvider; +} diff --git a/msal4j-brokers/src/test/java/labapi/UserQueryParameters.java b/msal4j-brokers/src/test/java/labapi/UserQueryParameters.java new file mode 100644 index 00000000..a5bafe8d --- /dev/null +++ b/msal4j-brokers/src/test/java/labapi/UserQueryParameters.java @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package labapi; + +import java.util.HashMap; +import java.util.Map; + +public class UserQueryParameters { + public static final String FEDERATION_PROVIDER = "federationprovider"; + public static final String AZURE_ENVIRONMENT = "azureenvironment"; + public Map parameters = new HashMap<>(); +} diff --git a/msal4j-brokers/src/test/java/labapi/UserSecret.java b/msal4j-brokers/src/test/java/labapi/UserSecret.java new file mode 100644 index 00000000..ff4b619a --- /dev/null +++ b/msal4j-brokers/src/test/java/labapi/UserSecret.java @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package labapi; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class UserSecret { + @JsonProperty("secret") + String secret; + + @JsonProperty("value") + String value; +} diff --git a/msal4j-brokers/src/test/java/test/ProofOfPossessionTest.java b/msal4j-brokers/src/test/java/test/ProofOfPossessionTest.java new file mode 100644 index 00000000..2f613958 --- /dev/null +++ b/msal4j-brokers/src/test/java/test/ProofOfPossessionTest.java @@ -0,0 +1,184 @@ +package test; + +import com.microsoft.aad.msal4j.*; +import com.microsoft.aad.msal4jbrokers.Broker; +import infrastructure.SeleniumExtensions; +import labapi.LabUserProvider; +import labapi.User; +import org.openqa.selenium.WebDriver; +import org.testng.Assert; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Collections; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +public class ProofOfPossessionTest { + private final static String MICROSOFT_AUTHORITY_ORGANIZATIONS = + "https://login.microsoftonline.com/organizations/"; + private final static String GRAPH_DEFAULT_SCOPE = "user.read"; + + private LabUserProvider labUserProvider; + + WebDriver seleniumDriver; + + public void setUp() { + labUserProvider = LabUserProvider.getInstance(); + } + + public void acquirePopToken_WithBroker() throws Exception { + User user = labUserProvider.getDefaultUser(); + + Broker broker = new Broker.Builder().supportWindows(true).build(); + + PublicClientApplication pca = createPublicClientApp(user, broker); + + IAuthenticationResult result = acquirePoPTokenUsernamePassword(pca, user, Collections.singleton(GRAPH_DEFAULT_SCOPE)); + + //A valid PoP access token should be returned if a broker was set + assertTokenResultNotNull(result); + } + + public void acquirePopToken_WithoutBroker() throws Exception { + User user = labUserProvider.getDefaultUser(); + + PublicClientApplication pca = createPublicClientApp(user); + + //Setting UserNamePasswordParameters.proofOfPossession without enabling the broker should result in an exception when trying to get a token + IAuthenticationResult result = acquirePoPTokenUsernamePassword(pca, user, Collections.singleton(GRAPH_DEFAULT_SCOPE)); + } + + public void acquirePopToken_BrowserAndBroker() throws Exception { + User user = labUserProvider.getDefaultUser(); + + seleniumDriver = SeleniumExtensions.createDefaultWebDriver(); + + //First, get a non-PoP (bearer) token through a browser + PublicClientApplication pcaWithoutBroker = createPublicClientApp(user); + + SystemBrowserOptions browserOptions = + SystemBrowserOptions + .builder() + .openBrowserAction(new SeleniumOpenBrowserAction(user, pcaWithoutBroker)) + .build(); + + IAuthenticationResult browserResult = acquireTokenInteractive(pcaWithoutBroker, browserOptions); + + assertTokenResultNotNull(browserResult); + + seleniumDriver.quit(); + + //Then, get a PoP token silently, using the cache that contains the non-PoP token + Broker broker = new Broker.Builder().supportWindows(true).build(); + + PublicClientApplication pcaWithBroker = createPublicClientApp(user, broker, pcaWithoutBroker.tokenCache().serialize()); + + IAuthenticationResult acquireSilentResult = acquireTokenSilent(pcaWithBroker, browserResult.account()); + + //Ensure that the silent request retrieved a new PoP token, rather than the cached non-Pop token + Assert.assertNotNull(acquireSilentResult); + Assert.assertNotEquals(acquireSilentResult.accessToken(), browserResult.accessToken()); + } + + private PublicClientApplication createPublicClientApp(User user) throws MalformedURLException { + return PublicClientApplication.builder(user.getAppId()) + .authority(MICROSOFT_AUTHORITY_ORGANIZATIONS) + .correlationId(UUID.randomUUID().toString()) + .build(); + } + + private PublicClientApplication createPublicClientApp(User user, Broker broker) throws MalformedURLException { + return PublicClientApplication.builder(user.getAppId()) + .authority(MICROSOFT_AUTHORITY_ORGANIZATIONS) + .correlationId(UUID.randomUUID().toString()) + .broker(broker) + .build(); + } + + private PublicClientApplication createPublicClientApp(User user, Broker broker, String cache) throws MalformedURLException { + return PublicClientApplication.builder(user.getAppId()) + .authority(MICROSOFT_AUTHORITY_ORGANIZATIONS) + .correlationId(UUID.randomUUID().toString()) + .setTokenCacheAccessAspect(new TokenPersistence(cache)) + .broker(broker) + .build(); + } + + private IAuthenticationResult acquirePoPTokenUsernamePassword(PublicClientApplication pca, User user, Set scopes) + throws URISyntaxException, ExecutionException, InterruptedException { + UserNamePasswordParameters parameters = UserNamePasswordParameters.builder( + scopes, user.getUpn(), + user.getPassword().toCharArray()) + .proofOfPossession(HttpMethod.GET, new URI("http://localhost"), null) + .build(); + + return pca.acquireToken(parameters).get(); + } + + private IAuthenticationResult acquireTokenInteractive(PublicClientApplication pca, SystemBrowserOptions browserOptions) + throws URISyntaxException { + InteractiveRequestParameters interactiveParams = InteractiveRequestParameters + .builder(new URI("http://localhost:8080")) + .scopes(Collections.singleton(GRAPH_DEFAULT_SCOPE)) + .systemBrowserOptions(browserOptions) + .build(); + + return pca.acquireToken(interactiveParams).join(); + } + + private IAuthenticationResult acquireTokenSilent(PublicClientApplication pca, IAccount account) throws URISyntaxException, MalformedURLException, ExecutionException, InterruptedException { + SilentParameters silentParams = SilentParameters.builder(Collections.singleton(GRAPH_DEFAULT_SCOPE), account) + .proofOfPossession(HttpMethod.GET, new URI("http://localhost"), null) + .build(); + + return pca.acquireTokenSilently(silentParams).get(); + } + + private void assertTokenResultNotNull(IAuthenticationResult result) { + Assert.assertNotNull(result); + Assert.assertNotNull(result.accessToken()); + Assert.assertNotNull(result.idToken()); + } + + class SeleniumOpenBrowserAction implements OpenBrowserAction { + + private User user; + private PublicClientApplication pca; + + SeleniumOpenBrowserAction(User user, PublicClientApplication pca) { + this.user = user; + this.pca = pca; + } + + public void openBrowser(URL url) { + seleniumDriver.navigate().to(url); + runSeleniumAutomatedLogin(user, pca); + } + } + + void runSeleniumAutomatedLogin(User user, AbstractClientApplicationBase app) { + SeleniumExtensions.performADLogin(seleniumDriver, user); + } + + static class TokenPersistence implements ITokenCacheAccessAspect { + String data; + + TokenPersistence(String data) { + this.data = data; + } + + @Override + public void beforeCacheAccess(ITokenCacheAccessContext iTokenCacheAccessContext) { + iTokenCacheAccessContext.tokenCache().deserialize(data); + } + + @Override + public void afterCacheAccess(ITokenCacheAccessContext iTokenCacheAccessContext) { + data = iTokenCacheAccessContext.tokenCache().serialize(); + } + } +} diff --git a/msal4j-sdk/README.md b/msal4j-sdk/README.md index 474499bc..fac64d38 100644 --- a/msal4j-sdk/README.md +++ b/msal4j-sdk/README.md @@ -16,7 +16,7 @@ Quick links: The library supports the following Java environments: - Java 8 (or higher) -Current version - 1.13.11 +Current version - 1.14.0 You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/master/changelog.txt). @@ -28,13 +28,12 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti com.microsoft.azure msal4j - 1.13.11 - + 1.14.0 ``` ### Gradle ```gradle -compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.13.11' +compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.14.0' ``` ## Usage diff --git a/msal4j-sdk/bnd.bnd b/msal4j-sdk/bnd.bnd index 0a14dff8..e9acb269 100644 --- a/msal4j-sdk/bnd.bnd +++ b/msal4j-sdk/bnd.bnd @@ -1,2 +1,2 @@ -Export-Package: com.microsoft.aad.msal4j;version="1.13.11" +Export-Package: com.microsoft.aad.msal4j;version="1.14.0" Automatic-Module-Name: com.microsoft.aad.msal4j diff --git a/msal4j-sdk/pom.xml b/msal4j-sdk/pom.xml index 847b32e7..6748d4bd 100644 --- a/msal4j-sdk/pom.xml +++ b/msal4j-sdk/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.microsoft.azure msal4j - 1.13.11 + 1.14.0 jar msal4j diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java index 22e8299f..0d3f231a 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java @@ -130,4 +130,15 @@ public class AuthenticationErrorCode { * slow response, and this may be resolvable by increasing timeouts. For more details, see https://aka.ms/msal4j-http-client */ public final static String HTTP_TIMEOUT = "http_timeout"; + + /** + * Indicates an error from the MSAL Java/MSALRuntime interop layer used by the Java Brokers package, + * and will generally just be forwarding an error message from the interop layer or MSALRuntime itself + */ + public final static String MSALRUNTIME_INTEROP_ERROR = "interop_package_error"; + + /** + * Indicates an error related to the MSAL Java Brokers package + */ + public final static String MSALJAVA_BROKERS_ERROR = "brokers_package_error"; } diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java index dcef23f5..1338467b 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java @@ -90,4 +90,7 @@ private ITenantProfile getTenantProfile() { private final Date expiresOnDate = new Date(expiresOn * 1000); private final String scopes; + + @Getter(value = AccessLevel.PACKAGE) + private final Boolean isPopAuthorization; } diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/HttpMethod.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/HttpMethod.java index 64af4605..9497ec3c 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/HttpMethod.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/HttpMethod.java @@ -4,17 +4,53 @@ package com.microsoft.aad.msal4j; /** - * Http request method. + * An enumerator representing common HTTP request methods. */ public enum HttpMethod { + /** + * The HTTP CONNECT method. + */ + CONNECT("CONNECT"), + + /** + * The HTTP DELETE method. + */ + DELETE("DELETE"), + /** * The HTTP GET method. */ - GET, + GET("GET"), + + /** + * The HTTP HEAD method. + */ + HEAD("HEAD"), + + /** + * The HTTP OPTIONS method. + */ + OPTIONS("OPTIONS"), /** * The HTTP POST method. */ - POST + POST("POST"), + + /** + * The HTTP PUT method. + */ + PUT("PUT"), + + /** + * The HTTP TRACE method. + */ + TRACE("TRACE"); + + public final String methodName; + + HttpMethod(String methodName) { + this.methodName = methodName; + } } diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/IBroker.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/IBroker.java index 919a8092..ab3f0ce0 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/IBroker.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/IBroker.java @@ -3,58 +3,87 @@ package com.microsoft.aad.msal4j; -import java.util.Set; +import com.nimbusds.jwt.JWTParser; + +import java.net.URL; import java.util.concurrent.CompletableFuture; /** * Used to define the basic set of methods that all Brokers must implement - * - * All methods are so they can be referenced by MSAL Java without an implementation, and by default simply throw an - * exception saying that a broker implementation is missing + *

+ * All methods are marked as default so they can be referenced by MSAL Java without an implementation, + * and most will simply throw an exception if not overridden by an IBroker implementation */ public interface IBroker { - /** - * checks if a IBroker implementation exists - */ - - default boolean isAvailable(){ - return false; - } /** * Acquire a token silently, i.e. without direct user interaction - * + *

* This may be accomplished by returning tokens from a token cache, using cached refresh tokens to get new tokens, * or via any authentication flow where a user is not prompted to enter credentials - * - * @param requestParameters MsalRequest object which contains everything needed for the broker implementation to make a request - * @return IBroker implementations will return an AuthenticationResult object */ - default IAuthenticationResult acquireToken(PublicClientApplication application, SilentParameters requestParameters) { + default CompletableFuture acquireToken(PublicClientApplication application, SilentParameters requestParameters) { throw new MsalClientException("Broker implementation missing", AuthenticationErrorCode.MISSING_BROKER); } /** * Acquire a token interactively, by prompting users to enter their credentials in some way - * - * @param requestParameters MsalRequest object which contains everything needed for the broker implementation to make a request - * @return IBroker implementations will return an AuthenticationResult object */ - default IAuthenticationResult acquireToken(PublicClientApplication application, InteractiveRequestParameters requestParameters) { + default CompletableFuture acquireToken(PublicClientApplication application, InteractiveRequestParameters parameters) { throw new MsalClientException("Broker implementation missing", AuthenticationErrorCode.MISSING_BROKER); } /** * Acquire a token silently, i.e. without direct user interaction, using username/password authentication - * - * @param requestParameters MsalRequest object which contains everything needed for the broker implementation to make a request - * @return IBroker implementations will return an AuthenticationResult object */ - default IAuthenticationResult acquireToken(PublicClientApplication application, UserNamePasswordParameters requestParameters) { + default CompletableFuture acquireToken(PublicClientApplication application, UserNamePasswordParameters parameters) { throw new MsalClientException("Broker implementation missing", AuthenticationErrorCode.MISSING_BROKER); } - default CompletableFuture removeAccount(IAccount account) { + default void removeAccount(PublicClientApplication application, IAccount account) throws MsalClientException { + throw new MsalClientException("Broker implementation missing", AuthenticationErrorCode.MISSING_BROKER); + } + + /** + * Returns whether a broker is available and ready to use on this machine, allowing the use of the methods + * in this interface and other broker-only features in MSAL Java + */ + default boolean isBrokerAvailable() { throw new MsalClientException("Broker implementation missing", AuthenticationErrorCode.MISSING_BROKER); } + + /** + * MSAL Java's AuthenticationResult requires several package-private classes that a broker implementation can't access, + * so this helper method can be used to create AuthenticationResults from within the MSAL Java package + */ + default IAuthenticationResult parseBrokerAuthResult(String authority, String idToken, String accessToken, + String accountId, String clientInfo, + long accessTokenExpirationTime, + boolean isPopAuthorization) { + + AuthenticationResult.AuthenticationResultBuilder builder = AuthenticationResult.builder(); + + try { + if (idToken != null) { + builder.idToken(idToken); + if (accountId != null) { + String idTokenJson = + JWTParser.parse(idToken).getParsedParts()[1].decodeToString(); + builder.accountCacheEntity(AccountCacheEntity.create(clientInfo, + Authority.createAuthority(new URL(authority)), JsonHelper.convertJsonToObject(idTokenJson, + IdToken.class), null)); + } + } + if (accessToken != null) { + builder.accessToken(accessToken); + builder.expiresOn(accessTokenExpirationTime); + } + + builder.isPopAuthorization(isPopAuthorization); + + } catch (Exception e) { + throw new MsalClientException(String.format("Exception when converting broker result to MSAL Java AuthenticationResult: %s", e.getMessage()), AuthenticationErrorCode.MSALJAVA_BROKERS_ERROR); + } + return builder.build(); + } } \ No newline at end of file diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java index 33e89eab..567cb280 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java @@ -105,6 +105,20 @@ public class InteractiveRequestParameters implements IAcquireTokenParameters { */ private boolean instanceAware; + /** + * The parent window handle used to open UI elements with the correct parent + * + * + * For browser scenarios and Windows console applications, this value should not need to be set + * + * For Windows console applications, MSAL Java will attempt to discover the console's window handle if this parameter is not set + * + * For scenarios where MSAL Java is responsible for opening UI elements (such as when using MSALRuntime), this parameter is required and an exception will be thrown if not set + */ + private long windowHandle; + + private PopParameters proofOfPossession; + private static InteractiveRequestParametersBuilder builder() { return new InteractiveRequestParametersBuilder(); } @@ -116,4 +130,23 @@ public static InteractiveRequestParametersBuilder builder(URI redirectUri) { return builder() .redirectUri(redirectUri); } + + //This Builder class is used to override Lombok's default setter behavior for any fields defined in it + public static class InteractiveRequestParametersBuilder { + + /** + * Sets the PopParameters for this request, allowing the request to retrieve proof-of-possession tokens rather than bearer tokens + * + * For more information, see {@link PopParameters} and https://aka.ms/msal4j-pop + * + * @param httpMethod a valid HTTP method, such as "GET" or "POST" + * @param uri the URI on the downstream protected API which the application is trying to access, e.g. https://graph.microsoft.com/beta/me/profile + * @param nonce a string obtained by calling the resource (e.g. Microsoft Graph) un-authenticated and parsing the WWW-Authenticate header associated with pop authentication scheme and extracting the nonce parameter, or, on subsequent calls, by parsing the Autheticate-Info header and extracting the nextnonce parameter. + */ + public InteractiveRequestParametersBuilder proofOfPossession(HttpMethod httpMethod, URI uri, String nonce) { + this.proofOfPossession = new PopParameters(httpMethod, uri, nonce); + + return this; + } + } } diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/PopParameters.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/PopParameters.java new file mode 100644 index 00000000..d72ab5a5 --- /dev/null +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/PopParameters.java @@ -0,0 +1,44 @@ +package com.microsoft.aad.msal4j; + +import java.net.URI; + +/** + * Contains parameters used to request a Proof of Possession (PoP) token in supported flows + */ +public class PopParameters { + + HttpMethod httpMethod; + URI uri; + String nonce; + + public HttpMethod getHttpMethod() { + return httpMethod; + } + + public URI getUri() { + return uri; + } + + public String getNonce() { + return nonce; + } + + PopParameters(HttpMethod httpMethod, URI uri, String nonce) { + validatePopAuthScheme(httpMethod, uri); + + this.httpMethod = httpMethod; + this.uri = uri; + this.nonce = nonce; + } + + /** + * Performs any minimum validation to confirm this auth scheme could be valid for a POP request + */ + void validatePopAuthScheme(HttpMethod httpMethod, URI uri) { + //At a minimum HTTP method and host must be non-null + if (httpMethod == null || uri == null || uri.getHost() == null) { + throw new MsalClientException( + "HTTP method and URI host must be non-null", AuthenticationErrorCode.MSALJAVA_BROKERS_ERROR); + } + } +} diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/PublicClientApplication.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/PublicClientApplication.java index 80fa1c31..e03b9c17 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/PublicClientApplication.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/PublicClientApplication.java @@ -8,6 +8,7 @@ import com.nimbusds.oauth2.sdk.id.ClientID; import org.slf4j.LoggerFactory; +import java.net.MalformedURLException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @@ -23,6 +24,8 @@ public class PublicClientApplication extends AbstractClientApplicationBase implements IPublicClientApplication { private final ClientAuthenticationPost clientAuthentication; + private IBroker broker; + private boolean brokerEnabled; @Override public CompletableFuture acquireToken(UserNamePasswordParameters parameters) { @@ -35,12 +38,20 @@ public CompletableFuture acquireToken(UserNamePasswordPar parameters, UserIdentifier.fromUpn(parameters.username())); - UserNamePasswordRequest userNamePasswordRequest = - new UserNamePasswordRequest(parameters, - this, - context); + CompletableFuture future; + + if (validateBrokerUsage(parameters)) { + future = broker.acquireToken(this, parameters); + } else { + UserNamePasswordRequest userNamePasswordRequest = + new UserNamePasswordRequest(parameters, + this, + context); - return this.executeRequest(userNamePasswordRequest); + future = this.executeRequest(userNamePasswordRequest); + } + + return future; } @Override @@ -111,17 +122,49 @@ public CompletableFuture acquireToken(InteractiveRequestP this, context); - CompletableFuture future = executeRequest(interactiveRequest); + CompletableFuture future; + + if (validateBrokerUsage(parameters)) { + future = broker.acquireToken(this, parameters); + } else { + future = executeRequest(interactiveRequest); + } + futureReference.set(future); + return future; } + @Override + public CompletableFuture acquireTokenSilently(SilentParameters parameters) throws MalformedURLException { + CompletableFuture future; + + if (validateBrokerUsage(parameters)) { + future = broker.acquireToken(this, parameters); + } else { + future = super.acquireTokenSilently(parameters); + } + + return future; + } + + @Override + public CompletableFuture removeAccount(IAccount account) { + if (brokerEnabled) { + broker.removeAccount(this, account); + } + + return super.removeAccount(account); + } + private PublicClientApplication(Builder builder) { super(builder); validateNotBlank("clientId", clientId()); log = LoggerFactory.getLogger(PublicClientApplication.class); this.clientAuthentication = new ClientAuthenticationPost(ClientAuthenticationMethod.NONE, new ClientID(clientId())); + this.broker = builder.broker; + this.brokerEnabled = builder.brokerEnabled; } @Override @@ -145,6 +188,22 @@ private Builder(String clientId) { super(clientId); } + private IBroker broker = null; + private boolean brokerEnabled = false; + + /** + * Implementation of IBroker that will be used to retrieve tokens + *

+ * Setting this will cause MSAL Java to use the given broker implementation to retrieve tokens from a broker (such as WAM/MSALRuntime) in flows that support it + */ + public PublicClientApplication.Builder broker(IBroker val) { + this.broker = val; + + this.brokerEnabled = this.broker.isBrokerAvailable(); + + return self(); + } + @Override public PublicClientApplication build() { @@ -156,4 +215,61 @@ protected Builder self() { return this; } } + + /** + * Used to determine whether to call into an IBroker instance instead of standard MSAL Java's normal interactive flow, + * and may throw exceptions or log messages if broker-only parameters are used when a broker is not enabled/available + */ + private boolean validateBrokerUsage(InteractiveRequestParameters parameters) { + + //Check if broker-only parameters are being used when a broker is not enabled. If they are, either throw an + // exception saying a broker is required, or provide a clear log message saying the parameter will be ignored + if (!brokerEnabled) { + if (parameters.proofOfPossession() != null) { + throw new MsalClientException( + "InteractiveRequestParameters.proofOfPossession should not be used when broker is not available, see https://aka.ms/msal4j-pop for more information", + AuthenticationErrorCode.MSALJAVA_BROKERS_ERROR ); + } + } + + return brokerEnabled; + } + + /** + * Used to determine whether to call into an IBroker instance instead of standard MSAL Java's normal username/password flow, + * and may throw exceptions or log messages if broker-only parameters are used when a broker is not enabled/available + */ + private boolean validateBrokerUsage(UserNamePasswordParameters parameters) { + + //Check if broker-only parameters are being used when a broker is not enabled. If they are, either throw an + // exception saying a broker is required, or provide a clear log message saying the parameter will be ignored + if (!brokerEnabled) { + if (parameters.proofOfPossession() != null) { + throw new MsalClientException( + "UserNamePasswordParameters.proofOfPossession should not be used when broker is not available, see https://aka.ms/msal4j-pop for more information", + AuthenticationErrorCode.MSALJAVA_BROKERS_ERROR ); + } + } + + return brokerEnabled; + } + + /** + * Used to determine whether to call into an IBroker instance instead of standard MSAL Java's normal silent flow, + * and may throw exceptions or log messages if broker-only parameters are used when a broker is not enabled/available + */ + private boolean validateBrokerUsage(SilentParameters parameters) { + + //Check if broker-only parameters are being used when a broker is not enabled. If they are, either throw an + // exception saying a broker is required, or provide a clear log message saying the parameter will be ignored + if (!brokerEnabled) { + if (parameters.proofOfPossession() != null) { + throw new MsalClientException( + "UserNamePasswordParameters.proofOfPossession should not be used when broker is not available, see https://aka.ms/msal4j-pop for more information", + AuthenticationErrorCode.MSALJAVA_BROKERS_ERROR ); + } + } + + return brokerEnabled; + } } \ No newline at end of file diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/SilentParameters.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/SilentParameters.java index 429c5dbb..c4c4e9fa 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/SilentParameters.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/SilentParameters.java @@ -6,6 +6,7 @@ import lombok.*; import lombok.experimental.Accessors; +import java.net.URI; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -64,6 +65,8 @@ public class SilentParameters implements IAcquireTokenParameters { */ private String tenant; + private PopParameters proofOfPossession; + private static SilentParametersBuilder builder() { return new SilentParametersBuilder(); @@ -114,4 +117,23 @@ private static Set removeEmptyScope(Set scopes){ } return updatedScopes; } + + //This Builder class is used to override Lombok's default setter behavior for any fields defined in it + public static class SilentParametersBuilder { + + /** + * Sets the PopParameters for this request, allowing the request to retrieve proof-of-possession tokens rather than bearer tokens + * + * For more information, see {@link PopParameters} and https://aka.ms/msal4j-pop + * + * @param httpMethod a valid HTTP method, such as "GET" or "POST" + * @param uri URI to associate with the token + * @param nonce optional nonce value for the token, can be empty or null + */ + public SilentParametersBuilder proofOfPossession(HttpMethod httpMethod, URI uri, String nonce) { + this.proofOfPossession = new PopParameters(httpMethod, uri, nonce); + + return this; + } + } } diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/UserNamePasswordParameters.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/UserNamePasswordParameters.java index cc4dab0c..f9df4454 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/UserNamePasswordParameters.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/UserNamePasswordParameters.java @@ -6,6 +6,7 @@ import lombok.*; import lombok.experimental.Accessors; +import java.net.URI; import java.util.Map; import java.util.Set; @@ -63,6 +64,8 @@ public class UserNamePasswordParameters implements IAcquireTokenParameters { */ private String tenant; + private PopParameters proofOfPossession; + public char[] password() { return password.clone(); } @@ -98,5 +101,20 @@ public UserNamePasswordParametersBuilder password(char[] password) { this.password = password.clone(); return this; } + + /** + * Sets the PopParameters for this request, allowing the request to retrieve proof-of-possession tokens rather than bearer tokens + * + * For more information, see {@link PopParameters} and https://aka.ms/msal4j-pop + * + * @param httpMethod a valid HTTP method, such as "GET" or "POST" + * @param uri URI to associate with the token + * @param nonce optional nonce value for the token, can be empty or null + */ + public UserNamePasswordParametersBuilder proofOfPossession(HttpMethod httpMethod, URI uri, String nonce) { + this.proofOfPossession = new PopParameters(httpMethod, uri, nonce); + + return this; + } } } From d4b5096f9e7926fb491978b45599dc96b46f8f83 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Thu, 26 Oct 2023 15:38:31 -0700 Subject: [PATCH 12/16] Version changes for 1.14.0 msal4j --- README.md | 6 +++--- msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml | 2 +- msal4j-sdk/src/samples/msal-obo-sample/pom.xml | 2 +- msal4j-sdk/src/samples/msal-web-sample/pom.xml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0ddb3d98..c2da4046 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Quick links: The library supports the following Java environments: - Java 8 (or higher) -Current version - 1.13.11 +Current version - 1.14.0 You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/main/msal4j-sdk/changelog.txt). @@ -28,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti com.microsoft.azure msal4j - 1.13.11 + 1.14.0 ``` ### Gradle ```gradle -implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.13.11' +implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.14.0' ``` ## Usage diff --git a/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml b/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml index 416d4d36..bcdded32 100644 --- a/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.13.11 + 1.14.0 com.nimbusds diff --git a/msal4j-sdk/src/samples/msal-obo-sample/pom.xml b/msal4j-sdk/src/samples/msal-obo-sample/pom.xml index 860ebce8..4c9aa9e8 100644 --- a/msal4j-sdk/src/samples/msal-obo-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-obo-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.13.11 + 1.14.0 com.nimbusds diff --git a/msal4j-sdk/src/samples/msal-web-sample/pom.xml b/msal4j-sdk/src/samples/msal-web-sample/pom.xml index bb6760fb..6838ae8c 100644 --- a/msal4j-sdk/src/samples/msal-web-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.13.11 + 1.14.0 com.nimbusds From b4e4f4bcb660c63e4ffc0324ca60a0570ff2db0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 07:35:51 -0800 Subject: [PATCH 13/16] Bump org.json:json in /msal4j-sdk/src/samples/msal-obo-sample (#740) Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20230227 to 20231013. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) - [Commits](https://github.com/douglascrockford/JSON-java/commits) --- updated-dependencies: - dependency-name: org.json:json dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- msal4j-sdk/src/samples/msal-obo-sample/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msal4j-sdk/src/samples/msal-obo-sample/pom.xml b/msal4j-sdk/src/samples/msal-obo-sample/pom.xml index 4c9aa9e8..74755dfa 100644 --- a/msal4j-sdk/src/samples/msal-obo-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-obo-sample/pom.xml @@ -33,7 +33,7 @@ org.json json - 20230227 + 20231013 org.projectlombok From 7288306422b48894a52031a88d19af8f0da0e85f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 07:36:04 -0800 Subject: [PATCH 14/16] Bump org.json:json in /msal4j-sdk/src/samples/msal-b2c-web-sample (#739) Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20230227 to 20231013. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) - [Commits](https://github.com/douglascrockford/JSON-java/commits) --- updated-dependencies: - dependency-name: org.json:json dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml b/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml index bcdded32..d055a7df 100644 --- a/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml @@ -33,7 +33,7 @@ org.json json - 20230227 + 20231013 From 000f4c9baa0353b18cf95880c882134183564fea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 07:36:18 -0800 Subject: [PATCH 15/16] Bump org.json:json in /msal4j-sdk/src/samples/msal-web-sample (#737) Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20230227 to 20231013. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) - [Commits](https://github.com/douglascrockford/JSON-java/commits) --- updated-dependencies: - dependency-name: org.json:json dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- msal4j-sdk/src/samples/msal-web-sample/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msal4j-sdk/src/samples/msal-web-sample/pom.xml b/msal4j-sdk/src/samples/msal-web-sample/pom.xml index 6838ae8c..b3bf334c 100644 --- a/msal4j-sdk/src/samples/msal-web-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-web-sample/pom.xml @@ -33,7 +33,7 @@ org.json json - 20230227 + 20231013 org.apache.commons From 2271537285b746901961fab1983a7f93575e215f Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Mon, 27 Nov 2023 15:17:06 -0800 Subject: [PATCH 16/16] Version updates for 1.14.4-beta release --- README.md | 6 +++--- changelog.txt | 6 ++++++ msal4j-sdk/README.md | 6 +++--- msal4j-sdk/bnd.bnd | 2 +- msal4j-sdk/pom.xml | 2 +- msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml | 2 +- msal4j-sdk/src/samples/msal-obo-sample/pom.xml | 2 +- msal4j-sdk/src/samples/msal-web-sample/pom.xml | 2 +- 8 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 905e3aff..3b26fbb2 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Quick links: The library supports the following Java environments: - Java 8 (or higher) -Current version - 1.14.2-beta +Current version - 1.14.4-beta You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/main/msal4j-sdk/changelog.txt). @@ -28,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti com.microsoft.azure msal4j - 1.14.2-beta + 1.14.4-beta ``` ### Gradle ```gradle -implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.14.2-beta' +implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.14.4-beta' ``` ## Usage diff --git a/changelog.txt b/changelog.txt index 4883f499..81735d47 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +Version 1.14.4-beta +============= +- Beta support for MSI in Azure Arc (#730) +- Beta support for MSI in Service Fabric (#729) +- Fix Cloud Shell parsing issue (#750) + Version 1.14.0 ============= - GA release of MSAL Java Brokers package diff --git a/msal4j-sdk/README.md b/msal4j-sdk/README.md index 4f801be3..d2e0069c 100644 --- a/msal4j-sdk/README.md +++ b/msal4j-sdk/README.md @@ -16,7 +16,7 @@ Quick links: The library supports the following Java environments: - Java 8 (or higher) -Current version - 1.14.2-beta +Current version - 1.14.4-beta You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/master/changelog.txt). @@ -28,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti com.microsoft.azure msal4j - 1.14.2-beta + 1.14.4-beta ``` ### Gradle ```gradle -compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.14.2-beta' +compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.14.4-beta' ``` ## Usage diff --git a/msal4j-sdk/bnd.bnd b/msal4j-sdk/bnd.bnd index c9609926..b8e2c6a8 100644 --- a/msal4j-sdk/bnd.bnd +++ b/msal4j-sdk/bnd.bnd @@ -1,2 +1,2 @@ -Export-Package: com.microsoft.aad.msal4j;version="1.14.2-beta" +Export-Package: com.microsoft.aad.msal4j;version="1.14.4-beta" Automatic-Module-Name: com.microsoft.aad.msal4j diff --git a/msal4j-sdk/pom.xml b/msal4j-sdk/pom.xml index dd3e6e21..a1dda198 100644 --- a/msal4j-sdk/pom.xml +++ b/msal4j-sdk/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.microsoft.azure msal4j - 1.14.2-beta + 1.14.4-beta jar msal4j diff --git a/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml b/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml index 0355a29b..4b3e6e24 100644 --- a/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-b2c-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.14.2-beta + 1.14.4-beta com.nimbusds diff --git a/msal4j-sdk/src/samples/msal-obo-sample/pom.xml b/msal4j-sdk/src/samples/msal-obo-sample/pom.xml index 79a00705..fd510aa7 100644 --- a/msal4j-sdk/src/samples/msal-obo-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-obo-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.14.2-beta + 1.14.4-beta com.nimbusds diff --git a/msal4j-sdk/src/samples/msal-web-sample/pom.xml b/msal4j-sdk/src/samples/msal-web-sample/pom.xml index b1a31e48..d0f33948 100644 --- a/msal4j-sdk/src/samples/msal-web-sample/pom.xml +++ b/msal4j-sdk/src/samples/msal-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.14.2-beta + 1.14.4-beta com.nimbusds