Skip to content

Commit e99f4c1

Browse files
authored
Merge pull request #48 from skyforce77/feat/credential-endpoint
[feature] VCI Credential endpoint
2 parents 34c31bd + 7277c35 commit e99f4c1

20 files changed

+1188
-8
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
<properties>
1313
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1414

15-
<authlete.java.common.version>3.68</authlete.java.common.version>
16-
<authlete.java.jaxrs.version>2.61</authlete.java.jaxrs.version>
15+
<authlete.java.common.version>3.75</authlete.java.common.version>
16+
<authlete.java.jaxrs.version>2.62</authlete.java.jaxrs.version>
1717
<javax.servlet-api.version>3.0.1</javax.servlet-api.version>
1818
<jersey.version>2.30.1</jersey.version>
1919
<jetty.version>9.4.27.v20200227</jetty.version>

src/main/java/com/authlete/jaxrs/server/api/OBBTokenTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ private static boolean needsProcessing(
7070

7171
// If the token request is a refresh token request.
7272
String grantType = requestParams.getFirst("grant_type");
73-
if (grantType != null && grantType.equals("refresht_token"))
73+
if (grantType != null && grantType.equals("refresh_token"))
7474
{
7575
// Because Open Baning Brasil prohibits refresh token rotation,
7676
// no new refresh token is issued by the refresh token request.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright (C) 2023 Authlete, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the
15+
* License.
16+
*/
17+
package com.authlete.jaxrs.server.api.vci;
18+
19+
20+
import javax.servlet.http.HttpServletRequest;
21+
import javax.ws.rs.WebApplicationException;
22+
import javax.ws.rs.core.HttpHeaders;
23+
import com.authlete.common.api.AuthleteApi;
24+
import com.authlete.common.dto.IntrospectionRequest;
25+
import com.authlete.common.dto.IntrospectionResponse;
26+
import com.authlete.jaxrs.BaseResourceEndpoint;
27+
import com.authlete.jaxrs.server.util.ExceptionUtil;
28+
29+
30+
public abstract class AbstractCredentialEndpoint extends BaseResourceEndpoint
31+
{
32+
protected String checkContentExtractToken(final HttpServletRequest request,
33+
final String requestContent)
34+
{
35+
if (requestContent == null)
36+
{
37+
throw ExceptionUtil.badRequestException("Missing request content.");
38+
}
39+
40+
final String accessToken = processAccessToken(request);
41+
if (accessToken == null)
42+
{
43+
throw ExceptionUtil.badRequestException("Missing access token.");
44+
}
45+
46+
return accessToken;
47+
}
48+
49+
50+
private String processAccessToken(final HttpServletRequest request)
51+
{
52+
// The value of the "Authorization" header.
53+
final String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
54+
55+
return super.extractAccessToken(authorization, null);
56+
}
57+
58+
59+
protected IntrospectionResponse introspect(final AuthleteApi api,
60+
final String accessToken)
61+
throws WebApplicationException
62+
{
63+
final IntrospectionRequest introspectionRequest = new IntrospectionRequest()
64+
.setToken(accessToken);
65+
66+
final IntrospectionResponse response = api.introspection(introspectionRequest);
67+
final String content = response.getResponseContent();
68+
69+
switch (response.getAction())
70+
{
71+
case BAD_REQUEST:
72+
throw ExceptionUtil.badRequestException(content);
73+
74+
case UNAUTHORIZED:
75+
throw ExceptionUtil.unauthorizedException(accessToken, content);
76+
77+
case FORBIDDEN:
78+
throw ExceptionUtil.forbiddenException(content);
79+
80+
case OK:
81+
return response;
82+
83+
case INTERNAL_SERVER_ERROR:
84+
default:
85+
throw ExceptionUtil.internalServerErrorException(content);
86+
}
87+
}
88+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright (C) 2023 Authlete, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the
15+
* License.
16+
*/
17+
package com.authlete.jaxrs.server.api.vci;
18+
19+
20+
import javax.servlet.http.HttpServletRequest;
21+
import javax.ws.rs.Consumes;
22+
import javax.ws.rs.POST;
23+
import javax.ws.rs.Path;
24+
import javax.ws.rs.WebApplicationException;
25+
import javax.ws.rs.core.Context;
26+
import javax.ws.rs.core.MediaType;
27+
import javax.ws.rs.core.Response;
28+
import com.authlete.common.api.AuthleteApi;
29+
import com.authlete.common.api.AuthleteApiFactory;
30+
import com.authlete.common.dto.CredentialBatchIssueRequest;
31+
import com.authlete.common.dto.CredentialBatchIssueResponse;
32+
import com.authlete.common.dto.CredentialBatchParseRequest;
33+
import com.authlete.common.dto.CredentialBatchParseResponse;
34+
import com.authlete.common.dto.CredentialIssuanceOrder;
35+
import com.authlete.common.dto.CredentialRequestInfo;
36+
import com.authlete.common.dto.IntrospectionResponse;
37+
import com.authlete.jaxrs.server.util.CredentialUtil;
38+
import com.authlete.jaxrs.server.util.ExceptionUtil;
39+
import com.authlete.jaxrs.server.util.ResponseUtil;
40+
import com.authlete.jaxrs.server.util.StringUtil;
41+
42+
43+
@Path("/api/batch_credential")
44+
public class BatchCredentialEndpoint extends AbstractCredentialEndpoint
45+
{
46+
@POST
47+
@Consumes(MediaType.APPLICATION_JSON)
48+
public Response post(@Context HttpServletRequest request,
49+
final String requestContent)
50+
{
51+
final AuthleteApi api = AuthleteApiFactory.getDefaultApi();
52+
53+
// Check request content
54+
final String accessToken = super.checkContentExtractToken(request, requestContent);
55+
56+
// Validate access token
57+
final IntrospectionResponse introspection = introspect(api, accessToken);
58+
59+
// Parse credential and make it an order
60+
final CredentialRequestInfo[] credential =
61+
credentialBatchParse(api, requestContent, accessToken);
62+
63+
final CredentialIssuanceOrder[] orders;
64+
try {
65+
orders = CredentialUtil.toOrder(introspection, credential);
66+
} catch (final CredentialUtil.UnknownCredentialFormatException e) {
67+
return ResponseUtil.badRequestJson(e.getJsonError());
68+
}
69+
70+
// Issue
71+
return credentialIssue(api, orders, accessToken);
72+
}
73+
74+
75+
private CredentialRequestInfo[] credentialBatchParse(final AuthleteApi api,
76+
final String requestContent,
77+
final String accessToken)
78+
throws WebApplicationException
79+
{
80+
final CredentialBatchParseRequest parseRequest =
81+
new CredentialBatchParseRequest()
82+
.setRequestContent(requestContent)
83+
.setAccessToken(accessToken);
84+
85+
final CredentialBatchParseResponse response =
86+
api.credentialBatchParse(parseRequest);
87+
final String content = response.getResponseContent();
88+
89+
switch (response.getAction())
90+
{
91+
case BAD_REQUEST:
92+
throw ExceptionUtil.badRequestExceptionJson(content);
93+
94+
case UNAUTHORIZED:
95+
throw ExceptionUtil.unauthorizedException(accessToken, content);
96+
97+
case FORBIDDEN:
98+
throw ExceptionUtil.forbiddenExceptionJson(content);
99+
100+
case OK:
101+
return response.getInfo();
102+
103+
case INTERNAL_SERVER_ERROR:
104+
default:
105+
throw ExceptionUtil.internalServerErrorException(content);
106+
}
107+
}
108+
109+
110+
private Response credentialIssue(final AuthleteApi api,
111+
final CredentialIssuanceOrder[] orders,
112+
final String accessToken)
113+
{
114+
final CredentialBatchIssueRequest credentialBatchIssueRequest = new CredentialBatchIssueRequest()
115+
.setAccessToken(accessToken)
116+
.setOrders(orders);
117+
118+
final CredentialBatchIssueResponse response = api.credentialBatchIssue(credentialBatchIssueRequest);
119+
final String content = response.getResponseContent();
120+
121+
switch (response.getAction())
122+
{
123+
case CALLER_ERROR:
124+
return ResponseUtil.badRequest(content);
125+
126+
case UNAUTHORIZED:
127+
return ResponseUtil.unauthorized(accessToken, content);
128+
129+
case FORBIDDEN:
130+
return ResponseUtil.forbiddenJson(content);
131+
132+
case OK:
133+
return ResponseUtil.okJson(content);
134+
135+
case INTERNAL_SERVER_ERROR:
136+
default:
137+
throw ExceptionUtil.internalServerErrorExceptionJson(content);
138+
}
139+
}
140+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (C) 2023 Authlete, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the
15+
* License.
16+
*/
17+
package com.authlete.jaxrs.server.api.vci;
18+
19+
20+
import java.util.Arrays;
21+
import com.authlete.common.types.StandardClaims;
22+
23+
24+
public enum CredentialDefinitionType
25+
{
26+
IDENTITY_CREDENTIAL("IdentityCredential", new String[]{
27+
StandardClaims.GIVEN_NAME,
28+
StandardClaims.FAMILY_NAME,
29+
StandardClaims.BIRTHDATE
30+
});
31+
32+
33+
final String id;
34+
final String[] claims;
35+
36+
37+
CredentialDefinitionType(final String typeId,
38+
final String[] claims)
39+
{
40+
this.id = typeId;
41+
this.claims = claims;
42+
}
43+
44+
45+
public String getId()
46+
{
47+
return id;
48+
}
49+
50+
51+
public String[] getClaims()
52+
{
53+
return claims;
54+
}
55+
56+
57+
public static CredentialDefinitionType byId(final String id)
58+
{
59+
return Arrays.stream(CredentialDefinitionType.values())
60+
.filter(format -> format.getId().equals(id))
61+
.findFirst()
62+
.orElse(null);
63+
}
64+
}

0 commit comments

Comments
 (0)