Skip to content

Add New Error Codes and Util Class for MSA DR Work #2533

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: fadi/MSA-DR-base
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public static class ClaimNames {
public static final String JWE_CRYPTO = "jwe_crypto";
public static final String SESSION_KEY_CRYPTO = "session_key_crypto";
public static final String PURPOSE = "purpose";

public static final String DEVICE_TOKEN = "device_token";
}

@SerializedName(ClaimNames.REFRESH_TOKEN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ public final class JwtRequestBody extends AbstractJwtRequest {
@SerializedName(ClaimNames.PURPOSE)
private String mPurpose;

@SerializedName(ClaimNames.DEVICE_TOKEN)
private String mDeviceToken;

public void setIat(final long iat) {
mIat = String.valueOf(iat);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public final class JwtRequestHeader extends AbstractJwtRequest {
// RSA using SHA256 - asymmetric key signing algorithm
public static final String ALG_VALUE_RS256 = "RS256";

public static final String KID_VALUE_ECDH = "ecdh";

@Setter(AccessLevel.NONE)
@SerializedName(ClaimNames.TYPE)
private String mType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
import com.google.gson.Gson;
import com.microsoft.identity.common.java.base64.Base64Util;
import com.microsoft.identity.common.java.logging.Logger;
import com.microsoft.identity.common.java.providers.oauth2.TokenRequest;
import com.microsoft.identity.common.java.util.MsaUtil;

import java.util.Arrays;


import javax.annotation.Nullable;

import lombok.NonNull;
import lombok.experimental.UtilityClass;
Expand Down Expand Up @@ -59,5 +66,38 @@ public static String generateJWT(@NonNull final JwtRequestHeader header,
Base64Util.encodeUrlSafeString(headerJson.getBytes(ENCODING_UTF8)) + "." + Base64Util.encodeUrlSafeString(bodyJson.getBytes(ENCODING_UTF8));
return encodedJwt;
}

/**
* Generate a JWT request header to be used in MSA DR flows.
* @param keyContext key context to include in header
* @return a request headers object
*/
@NonNull
public static JwtRequestHeader generateJwtRequestHeaderForMsaDR(@Nullable final byte[] keyContext) {
final JwtRequestHeader jwtRequestHeader = new JwtRequestHeader();
jwtRequestHeader.setType();
jwtRequestHeader.setAlg(JwtRequestHeader.ALG_VALUE_HS256);
jwtRequestHeader.setKId(JwtRequestHeader.KID_VALUE_ECDH);
jwtRequestHeader.setCtx(Arrays.toString(keyContext));

return jwtRequestHeader;
}

/**
* Generate a JWT request body to be used in MSA DR flows.
* @param deviceToken device token to include in body
* @return a request body object
*/
@NonNull
public static JwtRequestBody generateJwtRequestBodyForMsaDR(@Nullable final String audienceUrl, @Nullable final String nonce, @Nullable final String deviceToken) {
final JwtRequestBody jwtRequestBody = new JwtRequestBody();
jwtRequestBody.setAudience(audienceUrl);
jwtRequestBody.setNonce(nonce);
jwtRequestBody.setPurpose(MsaUtil.Companion.getJwtPurpose());
jwtRequestBody.setGrantType(TokenRequest.GrantTypes.DEVICE_AUTH);
jwtRequestBody.setDeviceToken(deviceToken);

return jwtRequestBody;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ public static class GrantTypes {
// e.g. used in Primary Refresh Token acquisition flows
public static final String JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer";
public static final String TRANSFER_TOKEN = "transfer_token";
public static final String DEVICE_AUTH = "device_auth";
}

public static class TokenType {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation.
// All rights reserved.
//
// This code is licensed under the MIT License.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package com.microsoft.identity.common.java.util

import com.microsoft.identity.common.java.authorities.Authority
import com.microsoft.identity.common.java.authorities.AzureActiveDirectoryAudience
import com.microsoft.identity.common.java.authorities.AzureActiveDirectoryAuthority
import java.util.Locale

/**
* Class for various MSA-related utility methods.
*/
class MsaUtil {

companion object {
val jwtPurpose = "v2sso"

/**
* Given an authority, check if this is an MSA request
*/
fun isMsaRequest(authority : Authority) : Boolean {
return if (authority !is AzureActiveDirectoryAuthority) {
false
} else {
// authority has been silently casted to AzureActiveDirectoryAuthority
val audience = authority.mAudience.tenantId.lowercase(Locale.ROOT)

// Check if audience is consumers or the MSA Mega Tenant
audience == AzureActiveDirectoryAudience.MSA_MEGA_TENANT_ID || audience == AzureActiveDirectoryAudience.CONSUMERS
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@
import com.google.gson.Gson;
import com.microsoft.identity.common.java.base64.Base64Util;
import com.microsoft.identity.common.java.providers.oauth2.TokenRequest;
import com.microsoft.identity.common.java.util.MsaUtil;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.util.Arrays;

@RunWith(JUnit4.class)
public class JwtUtilsTest {

Expand All @@ -59,4 +62,34 @@ public void testGenerateJWT() {
final String encodedJwt = JwtUtils.generateJWT(jwtRequestHeader, jwtRequestBody);
Assert.assertEquals(expectedJwt, encodedJwt);;
}

@Test
public void testGenerateJwtForMsaDR() {
final byte[] mockContext = "mockContext".getBytes();
final String mockAudience = "mockAudience";
final String mockNonce = "mockNonce";
final String mockDeviceToken = "mockDeviceToken";

final JwtRequestHeader expectedJwtRequestHeader = new JwtRequestHeader();
expectedJwtRequestHeader.setAlg(JwtRequestHeader.ALG_VALUE_HS256);
expectedJwtRequestHeader.setKId(JwtRequestHeader.KID_VALUE_ECDH);
expectedJwtRequestHeader.setCtx(Arrays.toString(mockContext));
final JwtRequestBody expectedJwtRequestBody = new JwtRequestBody();
expectedJwtRequestBody.setAudience(mockAudience);
expectedJwtRequestBody.setNonce(mockNonce);
expectedJwtRequestBody.setPurpose(MsaUtil.Companion.getJwtPurpose());
expectedJwtRequestBody.setGrantType(TokenRequest.GrantTypes.DEVICE_AUTH);
expectedJwtRequestBody.setDeviceToken(mockDeviceToken);
final String headerJson = new Gson().toJson(expectedJwtRequestHeader);
final String bodyJson = new Gson().toJson(expectedJwtRequestBody);

final String expectedJwt =
Base64Util.encodeUrlSafeString(headerJson.getBytes(ENCODING_UTF8)) + "." + Base64Util.encodeUrlSafeString(bodyJson.getBytes(ENCODING_UTF8));

final String encodedJwt = JwtUtils.generateJWT(
JwtUtils.generateJwtRequestHeaderForMsaDR(mockContext),
JwtUtils.generateJwtRequestBodyForMsaDR(mockAudience, mockNonce, mockDeviceToken)
);
Assert.assertEquals(expectedJwt, encodedJwt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation.
// All rights reserved.
//
// This code is licensed under the MIT License.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package com.microsoft.identity.common.java.util

import com.microsoft.identity.common.java.authorities.AccountsInOneOrganization
import com.microsoft.identity.common.java.authorities.AzureActiveDirectoryAudience
import com.microsoft.identity.common.java.authorities.AzureActiveDirectoryAuthority
import com.microsoft.identity.common.java.authorities.AzureActiveDirectoryB2CAuthority
import org.junit.Assert
import org.junit.Test

class MsaUtilTest {

@Test
fun isMsaRequestWithMsaMegaTenantAuthority() {
val msaMegaTenantAuthority = AzureActiveDirectoryAuthority(AccountsInOneOrganization(AzureActiveDirectoryAudience.MSA_MEGA_TENANT_ID))

Assert.assertTrue(MsaUtil.isMsaRequest(msaMegaTenantAuthority))
}

@Test
fun isMsaRequestWithConsumersAuthority() {
val consumersTenantAuthority = AzureActiveDirectoryAuthority(AccountsInOneOrganization(AzureActiveDirectoryAudience.CONSUMERS))

Assert.assertTrue(MsaUtil.isMsaRequest(consumersTenantAuthority))
}

@Test
fun isMsaRequestWithNonMsaAuthority() {
val nonMsaAuthority = AzureActiveDirectoryAuthority(AccountsInOneOrganization(AzureActiveDirectoryAudience.ALL))

Assert.assertFalse(MsaUtil.isMsaRequest(nonMsaAuthority))
}

@Test
fun isMsaRequestWithNonAzureAuthority() {
val nonAzureActiveDirectoryAuthority = AzureActiveDirectoryB2CAuthority("mockUrl")

Assert.assertFalse(MsaUtil.isMsaRequest(nonAzureActiveDirectoryAuthority))
}
}
Loading