package io.kaif.web;
import static org.hamcrest.CoreMatchers.is;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import io.kaif.model.clientapp.ClientApp;
import io.kaif.oauth.OauthAccessTokenDto;
import io.kaif.test.MvcIntegrationTests;
import io.kaif.web.support.AccessDeniedException;
public class OauthControllerTest extends MvcIntegrationTests {
private ClientApp clientApp = clientApp(accountCitizen("dev1"), "app1");
@Before
public void setUp() throws Exception {
}
@Test
public void authorize() throws Exception {
when(clientAppService.verifyRedirectUri(clientApp.getClientId(), "foo://callback")).thenReturn(
Optional.of(clientApp));
mockMvc.perform(get("/oauth/authorize").param("client_id", clientApp.getClientId())
.param("scope", "feed article")
.param("state", "123")
.param("response_type", "code")
.param("redirect_uri", "foo://callback"))
.andExpect(view().name("v1/authorize"))
.andExpect(status().isOk())
.andExpect(containsText("news feed"));
}
@Test
public void authorize_wrong_redirect_uri() throws Exception {
when(clientAppService.verifyRedirectUri(clientApp.getClientId(), "foo://callback")).thenReturn(
Optional.empty());
mockMvc.perform(get("/oauth/authorize").param("client_id", clientApp.getClientId())
.param("scope", "feed article")
.param("state", "123")
.param("response_type", "code")
.param("redirect_uri", "foo://callback"))
.andExpect(view().name("error"))
.andExpect(status().isBadRequest());
}
@Test
public void authorize_wrong_responseType() throws Exception {
when(clientAppService.verifyRedirectUri(clientApp.getClientId(), "foo://callback")).thenReturn(
Optional.of(clientApp));
mockMvc.perform(get("/oauth/authorize").param("client_id", clientApp.getClientId())
.param("scope", "feed article")
.param("state", "123")
.param("redirect_uri", "foo://callback"))
.andExpect(redirectedUrl(
"foo://callback?error=unsupported_response_type&error_description=response_type%20must%20be%20code&error_uri=https://kaif.io&state=123"))
.andExpect(status().isFound());
}
@Test
public void authorize_unexpected_server_error() throws Exception {
when(clientAppService.verifyRedirectUri(clientApp.getClientId(), "foo://callback")).thenThrow(
new RuntimeException("unexpected"));
mockMvc.perform(get("/oauth/authorize").param("client_id", clientApp.getClientId())
.param("scope", "feed article")
.param("state", "123")
.param("response_type", "code")
.param("redirect_uri", "foo://callback"))
.andExpect(redirectedUrl(
"foo://callback?error=server_error&error_description=unknown%20server%20error&error_uri=https://kaif.io&state=123"))
.andExpect(status().isFound());
}
@Test
public void authorize_missing_state() throws Exception {
when(clientAppService.verifyRedirectUri(clientApp.getClientId(), "foo://callback")).thenReturn(
Optional.of(clientApp));
mockMvc.perform(get("/oauth/authorize").param("client_id", clientApp.getClientId())
.param("scope", "feed article")
.param("response_type", "code")
.param("redirect_uri", "foo://callback"))
.andExpect(redirectedUrl(
"foo://callback?error=invalid_request&error_description=missing%20state&error_uri=https://kaif.io"))
.andExpect(status().isFound());
}
@Test
public void authorize_wrong_scope() throws Exception {
when(clientAppService.verifyRedirectUri(clientApp.getClientId(), "foo://callback")).thenReturn(
Optional.of(clientApp));
mockMvc.perform(get("/oauth/authorize").param("client_id", clientApp.getClientId())
.param("scope", "wrong---scope")
.param("state", "123")
.param("response_type", "code")
.param("redirect_uri", "foo://callback"))
.andExpect(redirectedUrl(
"foo://callback?error=invalid_scope&error_description=wrong%20scope&error_uri=https://kaif.io&state=123"))
.andExpect(status().isFound());
}
@Test
public void accessToken() throws Exception {
when(clientAppService.validateApp("client-id-foo", "client-secret-foo")).thenReturn(true);
when(clientAppService.createOauthAccessTokenByGrantCode("code1234",
"client-id-foo",
"foo://callback")).thenReturn(new OauthAccessTokenDto("oauth-token",
"public feed",
"Bearer"));
mockMvc.perform(post("/oauth/access-token").param("client_id", "client-id-foo")
.param("client_secret", "client-secret-foo")
.param("redirect_uri", "foo://callback")
.param("grant_type", "authorization_code")
.param("code", "code1234"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token", is("oauth-token")))
.andExpect(jsonPath("$.token_type", is("Bearer")))
.andExpect(jsonPath("$.scope", is("public feed")))
.andExpect(header().string("Cache-Control", "no-store"))
.andExpect(header().string("Pragma", "no-cache"));
}
@Test
public void accessToken_invalid_client() throws Exception {
when(clientAppService.validateApp("client-id-foo", "client-secret-foo")).thenReturn(false);
mockMvc.perform(post("/oauth/access-token").param("client_id", "client-id-foo")
.param("client_secret", "client-secret-foo")
.param("redirect_uri", "foo://callback")
.param("grant_type", "authorization_code")
.param("code", "code1234"))
.andExpect(status().isUnauthorized())
.andExpect(jsonPath("$.error", is("invalid_client")))
.andExpect(jsonPath("$.error_description", is("invalid client")));
}
@Test
public void accessToken_access_denied() throws Exception {
when(clientAppService.validateApp("client-id-foo", "client-secret-foo")).thenReturn(true);
when(clientAppService.createOauthAccessTokenByGrantCode("code1234",
"client-id-foo",
"foo://callback")).thenThrow(new AccessDeniedException());
mockMvc.perform(post("/oauth/access-token").param("client_id", "client-id-foo")
.param("client_secret", "client-secret-foo")
.param("redirect_uri", "foo://callback")
.param("grant_type", "authorization_code")
.param("code", "code1234"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error", is("invalid_grant")))
.andExpect(jsonPath("$.error_description", is("code is invalid")));
}
@Test
public void accessToken_wrong_grant_type() throws Exception {
mockMvc.perform(post("/oauth/access-token").param("client_id", "client-id-foo")
.param("client_secret", "client-secret-foo")
.param("redirect_uri", "foo://callback")
.param("grant_type", "wrong----type")
.param("code", "code1234"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error", is("unsupported_grant_type")))
.andExpect(jsonPath("$.error_description", is("grant_type must be authorization_code")));
}
@Test
public void accessToken_missing_client_id() throws Exception {
mockMvc.perform(post("/oauth/access-token").param("redirect_uri", "foo://callback")
.param("client_secret", "client-secret-foo")
.param("grant_type", "authorization_code")
.param("code", "code1234"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error", is("invalid_request")))
.andExpect(jsonPath("$.error_description", is("missing client_id")));
}
@Test
public void accessToken_missing_redirect_uri() throws Exception {
mockMvc.perform(post("/oauth/access-token").param("client_id", "client-id-foo")
.param("client_secret", "client-secret-foo")
.param("grant_type", "authorization_code")
.param("code", "code1234"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error", is("invalid_request")))
.andExpect(jsonPath("$.error_description", is("missing redirect_uri")));
}
@Test
public void accessToken_missing_code() throws Exception {
mockMvc.perform(post("/oauth/access-token").param("client_id", "client-id-foo")
.param("client_secret", "client-secret-foo")
.param("redirect_uri", "foo://callback")
.param("grant_type", "authorization_code"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error", is("invalid_request")))
.andExpect(jsonPath("$.error_description", is("missing code")));
}
@Test
public void directGrantCode() throws Exception {
when(clientAppService.directGrantCode("foo", "client-id-foo", "feed article", "foo://callback"))
.thenReturn("auth code");
mockMvc.perform(post("/oauth/authorize").param("oauthDirectAuthorize", "foo")
.param("client_id", "client-id-foo")
.param("redirect_uri", "foo://callback")
.param("scope", "feed article")
.param("state", "123"))
.andExpect(redirectedUrl("foo://callback?code=auth%20code&state=123"))
.andExpect(status().isFound())
.andExpect(header().string("Cache-Control", "no-store"))
.andExpect(header().string("Pragma", "no-cache"));
}
@Test
public void directGrantCode_unexpected_server_error() throws Exception {
when(clientAppService.directGrantCode("foo", "client-id-foo", "feed article", "foo://callback"))
.thenThrow(new RuntimeException("unexpected"));
mockMvc.perform(post("/oauth/authorize").param("oauthDirectAuthorize", "foo")
.param("client_id", "client-id-foo")
.param("redirect_uri", "foo://callback")
.param("scope", "feed article")
.param("state", "123 456"))
.andExpect(status().isFound())
.andExpect(redirectedUrl(
"foo://callback?error=server_error&error_description=unknown%20server%20error&error_uri=https://kaif.io&state=123%20456"));
}
@Test
public void directGrantCode_access_denied() throws Exception {
when(clientAppService.directGrantCode("foo", "client-id-foo", "feed article", "foo://callback"))
.thenThrow(new AccessDeniedException());
mockMvc.perform(post("/oauth/authorize").param("oauthDirectAuthorize", "foo")
.param("client_id", "client-id-foo")
.param("scope", "feed article")
.param("state", "123 456")
.param("redirect_uri", "foo://callback"))
.andExpect(redirectedUrl(
"foo://callback?error=access_denied&error_description=access%20denied&error_uri=https://kaif.io&state=123%20456"))
.andExpect(status().isFound());
}
@Test
public void directGrantCode_grantDeny() throws Exception {
mockMvc.perform(post("/oauth/authorize").param("oauthDirectAuthorize", "foo")
.param("grantDeny", "true")
.param("client_id", "client-id-foo")
.param("scope", "feed article")
.param("state", "123 456")
.param("redirect_uri", "foo://callback"))
.andExpect(redirectedUrl(
"foo://callback?error=access_denied&error_description=access%20denied&error_uri=https://kaif.io&state=123%20456"))
.andExpect(status().isFound());
}
}