Skip to content

Commit

Permalink
Fix scopes joining
Browse files Browse the repository at this point in the history
Multiple OAuth2 scopes should be space-separated
  • Loading branch information
lukasz-walkiewicz authored and kokosing committed Apr 30, 2021
1 parent b8bcec2 commit d69b6c9
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.oauth.AccessTokenRequestParams;
import com.github.scribejava.core.oauth.OAuth20Service;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import io.airlift.http.client.HttpClient;

Expand Down Expand Up @@ -74,7 +73,7 @@ public AccessToken getAccessToken(String code, URI callbackUri)

// Callback URI must be relative to client's view of the server.
// For example, the client may be accessing the server through an HTTP proxy.
private static class DynamicCallbackOAuth2Service
static class DynamicCallbackOAuth2Service
extends OAuth20Service
{
public DynamicCallbackOAuth2Service(OAuth2Config config, HttpClient httpClient)
Expand All @@ -84,7 +83,7 @@ public DynamicCallbackOAuth2Service(OAuth2Config config, HttpClient httpClient)
config.getClientId(),
config.getClientSecret(),
null,
Joiner.on(",").join(config.getScopes()),
String.join(" ", config.getScopes()),
"code",
null,
null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.server.security.oauth2;

import com.github.scribejava.core.model.OAuth2AccessToken;
import io.airlift.http.client.HttpClient;
import io.airlift.http.client.HttpClientConfig;
import io.airlift.http.client.jetty.JettyHttpClient;
import io.airlift.units.Duration;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SigningKeyResolver;
import io.trino.server.security.jwt.JwkService;
import io.trino.server.security.jwt.JwkSigningKeyResolver;
import io.trino.server.security.oauth2.ScribeJavaOAuth2Client.DynamicCallbackOAuth2Service;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.net.URI;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.google.common.io.Resources.getResource;
import static io.airlift.testing.Closeables.closeAll;
import static io.trino.server.security.oauth2.TokenEndpointAuthMethod.CLIENT_SECRET_BASIC;
import static org.assertj.core.api.Assertions.assertThat;

public class TestDynamicCallbackOAuth2Service
{
private static final String CLIENT_ID = "client";
private static final String CLIENT_SECRET = "secret";

private final TestingHydraIdentityProvider hydraIdP = new TestingHydraIdentityProvider();
private final HttpClient httpClient = new JettyHttpClient(new HttpClientConfig()
.setTrustStorePath(getResource("cert/localhost.pem").getPath()));
private String hydraUrl;
private SigningKeyResolver signingKeyResolver;

@BeforeClass
public void setUp()
{
hydraIdP.start();
hydraUrl = "https://localhost:" + hydraIdP.getHydraPort();
hydraIdP.createClient(
CLIENT_ID,
CLIENT_SECRET,
CLIENT_SECRET_BASIC,
"https://localhost:8080",
"https://localhost:8080/oauth2/callback");
signingKeyResolver = new JwkSigningKeyResolver(new JwkService(
URI.create(hydraUrl + "/.well-known/jwks.json"),
httpClient,
new Duration(5, TimeUnit.MINUTES)));
}

@AfterClass(alwaysRun = true)
public void tearDown()
throws Exception
{
closeAll(hydraIdP, httpClient);
}

@Test
public void testMultipleScopes()
throws Exception
{
DynamicCallbackOAuth2Service service = new DynamicCallbackOAuth2Service(
new OAuth2Config()
.setAuthUrl(hydraUrl + "/oauth2/auth")
.setTokenUrl(hydraUrl + "/oauth2/token")
.setJwksUrl(hydraUrl + "/.well-known/jwks.json")
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setScopes("openid,offline"),
httpClient);

OAuth2AccessToken token = service.getAccessTokenClientCredentialsGrant();

Claims claims = Jwts.parser()
.setSigningKeyResolver(signingKeyResolver)
.parseClaimsJws(token.getAccessToken())
.getBody();
assertThat(claims.get("scp", List.class)).containsExactlyInAnyOrder("openid", "offline");
}
}

0 comments on commit d69b6c9

Please sign in to comment.