protect-web-api: client and api - #12
Conversation
| description: "This sample demonstrates a Java Spring web application calling a Java Spring web API that is secured using Azure AD" | ||
| azureDeploy: <ENTER_FULLY_QUALIFIED_URL_TO_AN_AZURE_RESOURCE_MANAGER> | ||
| extendedZipContent: <FILES_OR_FOLDERS_WITH_TWO_ABSOLUTE_PATHS_TO_INCLUDE_WITH_ZIP:PATH(NAME_IN_THE_REPO), TARGET(NAME_IN_THE_ZIP)> | ||
| extensions: <ENTER_CONTENT_THAT_OTHER_TEAMS_CAN_USE_TO_IDENTIFY_SAMPLES> |
There was a problem hiding this comment.
| extensions: <ENTER_CONTENT_THAT_OTHER_TEAMS_CAN_USE_TO_IDENTIFY_SAMPLES> | |
| extensions: <ENTER_CONTENT_THAT_OTHER_TEAMS_CAN_USE_TO_IDENTIFY_SAMPLES> |
Reminder to remove these
| - [More information](#more-information) | ||
| - [Community Help and Support](#community-help-and-support) | ||
| - [Contributing](#contributing) | ||
|  |
There was a problem hiding this comment.
|  | |
|  |
reminder to remove
| @@ -0,0 +1,337 @@ | |||
| --- | |||
There was a problem hiding this comment.
Update the home landing page of the whole tutorial
| - Ensure that the **My APIs** tab is selected. | ||
| - In the list of APIs, select the API `java-servlet-resource-api`. | ||
| - In the **Delegated permissions** section, select the **Access 'java-servlet-resource-api'** in the list. Use the search box if necessary. | ||
| - Select the **Add permissions** button at the bottom. |
|
|
||
| ```Shell | ||
| cd project-directory | ||
| cd client-web-api |
| - Upon successful completion of the sign-in flow, you should be redirected to the home page (`sign in status`) or `token details` page, depending on which button triggered your sign-in flow. | ||
| - Note the context-sensitive button now says `Sign out` and displays your username to its left. | ||
| - If you are on the home page, you'll see an option to click **ID Token Details**: click it to see some of the ID token's decoded claims. | ||
| - You can also use the button on the top right to sign out. The status page will reflect this. |
There was a problem hiding this comment.
Would be good to add a step for "call api"
|
|
||
| 1. Find the resulting `.war` file in `./resource-api/target/msal4j-servlet-api.war` and deploy it to Tomcat or any other J2EE container solution. To deploy to Tomcat, copy this `.war` file to the `/webapps/` directory in your Tomcat installation directory and start the Tomcat server. | ||
| 1. Ensure that the context path that the app is served on is `/msal4j-servlet-client` (or change the `app.homePage` value in your [authentication.properties](src/main/resources/authentication.properties) file and in the AAD app registration). If you change the properties file, you'll needs to repeat step 3 above (maven clean and package). | ||
| 1. Open your browser and navigate to `http://localhost:8080/msal4j-servlet-client/` |
There was a problem hiding this comment.
Getting error: Server returned HTTP response code: 403 for URL: http://localhost:8080/msal4j-servlet-api/api/date
|
|
||
| ## About the code | ||
|
|
||
| This sample contains two projects. The client uses **MSAL for Java (MSAL4J)** to sign a user in and obtain a token for your API. The client side app is similar to the call-graph sample in chapter 2. The API app processes the incoming request and verifies that it has a valid access token. It leverages Nimbusds library to process and verify the incoming token. Please see the `resource-api/src/main/java.../helpers/JwtVerifier.java` for how the JwtVerifier is set up, and `resource-api/src/main/java.../servlets/AzureAccessTokenFilter.java` |
|
|
||
| ## About the code | ||
|
|
||
| This sample contains two projects. The client uses **MSAL for Java (MSAL4J)** to sign a user in and obtain a token for your API. The client side app is similar to the call-graph sample in chapter 2. The API app processes the incoming request and verifies that it has a valid access token. It leverages Nimbusds library to process and verify the incoming token. Please see the `resource-api/src/main/java.../helpers/JwtVerifier.java` for how the JwtVerifier is set up, and `resource-api/src/main/java.../servlets/AzureAccessTokenFilter.java` |
| ######################################################################################## | ||
| #For V1 tokens, uncomment the two values (authority and client ID) in the section below: | ||
|
|
||
| aad.clientId=api://Enter_Your_Client_ID_Here |
| <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> | ||
| <link rel="stylesheet" href="./static/style.css"> | ||
| <link rel="icon" type="image/x-icon" href="./static/favicon.ico"> | ||
| <title>Authentication: Use MSAL Java to sign in users in your Azure Active Directory tenant</title> |
| aad.clientId=api://Enter_Your_Client_ID_Here | ||
| aad.authority=https://sts.windows.net/Enter_Your_Tenant_ID_Here/ | ||
|
|
||
|
|
derisen
left a comment
There was a problem hiding this comment.
LGTM! Left a bunch of comments for the README
| * routes | ||
| */ | ||
| @WebFilter(filterName = "AzureAccessTokenFilter", urlPatterns = "/*") | ||
| public class AzureAccessTokenFilter implements Filter { |
| // make an instance of the verifier (found in helpers.JwtVerifier) | ||
| // use AUTHORITY value from config for issuer. | ||
| // use CLIENT_ID value from config for audience. | ||
| // use SCOPES value from config for scopes. |
There was a problem hiding this comment.
heavil;y comment all that will be validated
|
|
||
| /** | ||
| * This class defines the endpoint for showing the graph /me endpoint | ||
| * This is here simply to demonstrate the graph call. |
| @@ -0,0 +1,49 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| @Override | ||
| protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) | ||
| throws ServletException, IOException { | ||
| logger.log(Level.FINE, "Request has come with params {0}", req.getQueryString()); |
There was a problem hiding this comment.
Reminder for removing this line log statement.
| * format:'{group-id} /protected-route-1 | ||
| * /protected-route-2 /protected-route-N' | ||
| */ | ||
| public ProtectedRoutes(List<String> authRoutes, List<String> groupNameAndRoutes, List<String> roleNameAndRoutes, |
There was a problem hiding this comment.
Seems like groupNameAndRoutes and roleNameAndRoutes never get used?
| import java.io.InputStreamReader; | ||
| import java.net.HttpURLConnection; | ||
|
|
||
|
|
| } | ||
| } | ||
|
|
||
| private static class NetClient { |
There was a problem hiding this comment.
Can we use the Graph SDK here instead?
| /** | ||
| * AAD has many aliases. We need to account for this when verifying | ||
| * @return | ||
| */ | ||
|
|
||
| private void issuerVerifier() { | ||
|
|
||
| } |
| // RSA keys sourced from the JWK set URL | ||
| return new JWSVerificationKeySelector<>(expectedJWSAlg, keySource); | ||
| } | ||
|
|
| // if the verification is successful, you'll receive a JWTClaimsSet | ||
| JWTClaimsSet claimsSet = verifier.getJWTClaimsSet(accessToken); | ||
| // put it into the request so the request handling servlet can make use of its claims as neeed. | ||
| req.setAttribute("accessToken", claimsSet); |
There was a problem hiding this comment.
nit: consider renaming this attribute to accessTokenClaims
| String msg = String.format("An error occurred when validating the JWT signature: %s", e.getMessage()); | ||
| logger.log(Level.WARNING, msg); | ||
| HttpServletResponse httpResponse = (HttpServletResponse) res; | ||
| PrintWriter out; | ||
| out = httpResponse.getWriter(); | ||
| httpResponse.setStatus(403); | ||
| out.write(msg); | ||
| out.close(); |
There was a problem hiding this comment.
Do you know what kind of information Spring/Identity Web return to the caller when JWT validation fails? I ask because we might not want to return any information about the JWT validation error to callers, as this could be a security vulnerability.
Also we can discuss this in person but I think 401 status code might be a bit more appropriate here. I see 401 as saying, "Unauthenticated" or "Could not validate your identity", while 403 is "Unauthorized" or "Validated your identity, but you don't have the permissions to access this specific resource"
| PrintWriter out; | ||
| out = resp.getWriter(); | ||
| resp.setContentType("text/html"); | ||
| resp.setStatus(200); | ||
| out.write(dateAsJson()); | ||
| out.close(); |
There was a problem hiding this comment.
Suggested:
resp.setContentType("text/html");
resp.setStatus(200);
PrintWriter out = resp.getWriter();
out.write(dateAsJson());
out.close();
No description provided.