I have an oauth Authserver app,a web app(enabled oauth sso),an android app(needs to access rest resources in the web app)
I am trying to implement spring-oauth2 taking example of oauth-vanilla(latest one with no jwt) spring boot sample project.The project has the following client configuration
security.oauth2.client.clientId: acme
security.oauth2.client.clientSecret: acmesecret
security.oauth2.client.authorized-grant-types: authorization_code,refresh_token,password
security.oauth2.client.scope: openid
so the logic flow goes like(correct me if am wrong) user attempts access to the ui app -->zuul proxied redirect(with client details) to authentication server-->login with credentials (protected /authorize url)-->authorize the scopes-->return with the token/authorization code.
How to eliminate the user authorization step(for android app purpose i want resource owner apporach).I changed my client configuration like this to follow
clients.inMemory().withClient("****").secret("*****").authorities("ROLE_USER")
.authorizedGrantTypes("password", "refresh_token").scopes("read", "write");
but I am getting error from authserver app (DefaultRedirectResolver.java)
"A redirect_uri can only be used by implicit or authorization_code
grant types."
if i have my security.oauth properties a below in my web-ui app
security:
oauth2:
client:
accessTokenUri: http://localhost:9097/uaa/oauth/token
userAuthorizationUri: http://localhost:9097/uaa/oauth/authorize
clientId: ****
clientSecret: ****
resource:
userInfoUri: http://localhost:9097/uaa/user
for single sign on ,can resource owner password approach be used or not ? if so what should i change as part of configuration?
"A redirect_uri can only be used by implicit or authorization_code
grant types."
I'm not sure about the "redirect_uri" but I don't think you can't get single sign on with the password flow. SSO is achieved by creating a session on the authorization server so that a client (same or different) won't have to authenticate again if the authorization server already has an authenticated session. The password flow doesn't create a session on the authentication server...it only gets tokens.
You must specify authorization_code or implicit grant types on .authorizedGrantTypes(), for they are also redirect types.
clients.inMemory().withClient("****").secret("*****").authorities("ROLE_USER")
.authorizedGrantTypes("authorization_code", "password", "refresh_token").scopes("read", "write");
or
clients.inMemory().withClient("****").secret("*****").authorities("ROLE_USER")
.authorizedGrantTypes("implicit", "password", "refresh_token").scopes("read", "write");
Source:
DefautlRedirectResolver
Related
I have simple resource server application with spring boot, this is yaml file:
server:
port: 8081
servlet:
context-path: /resource-server-jwt
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8083/auth/realms/rasool
Now, i want to make change in configuration or code to force spring security to validate JWT token with calling introspection endpoint of authorization server instead of local validation with keys, but i didn't find any way as spring security docs says.
Spring-boot spring.security.oauth2.resourceserver.jwt.* configuration properties are for JWT decoder.
For token introspection, use spring.security.oauth2.resourceserver.opaque-token.* properties instead (token being in whatever format, including JWT). "opaque" means that tokens are considered a "black-box" by resource-server which delegates validataion and attributes retrieval to authorization-server on introspection endpoint:
server:
port: 8081
servlet:
context-path: /resource-server-jwt
spring:
security:
oauth2:
resourceserver:
opaque-token:
introspection-uri: http://localhost:8083/auth/realms/rasool/protocol/openid-connect/token/introspect
client-id: change-me
client-secret: change-me
Introspection uri from .well-known/openid-configuration
If you are using Java configurationn the switch is about the same: replace http.oauth2ResourceServer().jwt()... with http.oauth2ResourceServer().opaqueToken()...
A few notes about declared clients on authorisation-server
Resource-servers introspect token on authorisation-server introspection endpoint using client-credentials flow: for each and every request it process, resource-servers will send a request to authorization-server to get token details. This can have serious performance impact. Are you sure you want to switch to token introspection?
As a consequence, in the properties above, you must configure a client with:
"Access Type" set to confidential
"Service Accounts Enabled" activated
Create one if you don't have yet. You'll get client-secret from "credentials tab" once configuration saved.
Note you should have other (public) clients to identify users (from web / mobile apps or REST client) and query your resource-server on behalf of those users.
From the authorization-server point of view, this means that access-tokens will be issued to a (public) client and introspected by antoher (confidential) client.
Complete working sample here
It does a few things useful for resource-servers:
authorities mapping (choose attributes to parse user authorities from, prefix & case processing)
CORS configuration
stateless-session management
CSRF with Cookie repo
anonymous enabled for a list of configured public routes
401 (unauthorized) instead of 302 (redirect to login) when trying to access protected resources with missing or invalid Authorization
I have a working Cognito app client that utilizes user pool that is wired to use SAML. It accesses Azure AD as IdP. When I click "Launch Hosted UI" it properly redirects me to the login screen and upon authentication attempts to load my callback URL.
Now I want to wire this with a Spring Boot app.
I found this example developed by Joe Grandja that is using spring-security-saml2-service-provider to connect to a simple IdP.
The example is very compelling because all I really need to do is to provide correct configuration that in example provided like this:
spring:
security:
saml2:
relyingparty:
registration:
simplesamlphp:
signing.credentials:
- private-key-location: "classpath:credentials/rp-private.key"
certificate-location: "classpath:credentials/rp-certificate.crt"
identityprovider:
entity-id: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php
verification.credentials:
- certificate-location: "classpath:credentials/idp-certificate.crt"
sso-url: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php
However I'm lost at how to map information available to me from Cognito to these settings? For example values in signing.credentials?
Here's the list of settings I get from Cognito (all keys/names are bogus):
Pool Id: us-west-2_1B1AHf00
Pool ARN: arn:aws:cognito-idp:us-west-2:1234567898:userpool/us-west-2_1B1AHf00
Domain: https://blah-foo.auth.us-west-2.amazoncognito.com
App client: blahfoo-client
App client ID: 1b1l1a2f8oo83456c
Callback URL: http://localhost:8080
Login URL: https://blah-foo.auth.us-west-2.amazoncognito.com/login?client_id=1b1l1a2f8oo83456c&response_type=code&scope=email+openid&redirect_uri=http://localhost:8080
I also have a SAML-formatted file I got back from IdP but that is already plugged into Cognito so why would I put anything from it into the app configuration?
I wonder if part of spring-security-saml2-service-provider is to assemble that login URL and if I can get away with less settings that are given in the example?
Any pointers will be greatly appreciated
The signing.credentials section is if your app needs to sign things like an AuthnRequest. They are credentials that you own.
The items under identityprovider are things that Cognito would provide.
For Spring Boot 2.4+, if Cognito supports a SAML metadata endpoint, then you can provide that and Spring Security will discover the rest:
spring:
security:
saml2:
relyingparty:
registration:
simplesamlphp:
identityprovider:
metadata-uri: classpath:cognito/metadata/file/location
Or, for earlier versions, you can use RelyingPartyRegistrations:
#Bean
RelyingPartyRegistrationRepository registrations() {
String location = "classpath:cognito/metadata/file/location";
RelyingPartyRegistration registration =
RelyingPartyRegistrations.fromMetadataLocation(location)
.build();
return new InMemoryRelyingPartyRegistration(registration);
}
That said, the information that you've posted about Cognito's authentication endpoint appears OAuth-based, especially the Login URL. You may instead consider configuring your app for OAuth 2.0 and pointing at Cognito's OAuth endpoint.
I am posting the solution as a separate answer however credit and accepted answer go to #jzheaux
Basically the comment section provides the much needed hint: Even if you are wiring SAML-based Identity provider you will wire up Cognito using OAuth information given to you in the AWS console for User Pool
In my specific case the application.yaml then looks like this:
spring:
security:
oauth2:
client:
registration:
cognito:
client-id: 1ab2cd34efghi5jk6klmno7p8
client-secret: *********
scope: openid
redirect-uri: http://localhost:8080/login/oauth2/code/cognito
clientName: foobar-sandbox
provider:
cognito:
issuerUri: https://cognito-idp.us-west-2.amazonaws.com/us-west-2_abCDeFGHI
user-name-attribute: cognito:username
client-secret is found in General settings -> App clients -> Show Details
I have a spring application that verifies a JWT token on the rest endpoint.
Using SecurityChain
.oAuth2ResourceServer()
.jwt()
This seems to create a JwtAuthenticationToken in the ReactiveSecurityContextHolder.
I then want to flow the input from this endpoint where the client is authenticated by checking the bearer token. And then call another rest service using a webClient. This web client needs to authenticate with grant type password with the external service using a different OAuth server and get is own bearer token.
The problem is that the web client uses the ReactiveSecurityContextHolder that contains the authenticated JWT. And tries to use this information rather than connect and authenticate my app to the rest endpoint.
I have set up the Yaml to register my client
spring:
security:
oauth2:
client:
registration:
Myapp:
client-id:
client-secret:
token-uri:
authorization-grant-type:
Then adding a filter function of
ServerOAuth2AuthorizedClientExchangeFilterFunction
But I get principalName cannot be empty as it seems to reuse the security context from verifying the caller on the rest endpoint in my application.
How should it be designed or samples to show how you can use different security contexts or get tokens differently between service to service calls?
You are correct that the design of ServerOAuth2AuthorizedClientExchangeFilterFunction is designed to be based on the currently-authorized client, which you've explained that you don't want to use in this case.
You've indicated that you want to use the client's credentials as the username and password for the Resource Owner Password Grant. However, there's nothing in Spring Security that is going to do that.
However, you can use WebClientReactivePasswordTokenResponseClient directly in order to formulate the custom request yourself.
Briefly, this would be a custom ExchangeFilterFunction that would look something like:
ClientRegistrationRespository clientRegistrations;
ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest>
accessTokenResponseClient = new WebClientReactivePasswordTokenResponseClient();
Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
return this.clientRegistrations.findByRegistrationId("registration-id")
.map(clientRegistration -> new OAuth2PasswordGrantRequest(
clientRegistration,
clientRegistration.getClientId(),
clientRegistration.getClientSecret())
.map(this.accessTokenResponseClient::getTokenResponse)
.map(tokenResponse -> ClientRequest.from(request)
.headers(h -> h.setBearerAuth(tokenResponse.getAccessToken().getTokenValue())
.build())
.flatMap(next::exchange);
}
(For brevity, I've removed any error handling.)
The above code takes the following steps:
Look up the appropriate client registration -- this contains the provider's endpoint as well as the client id and secret
Construct an OAuth2PasswordGrantRequest, using the client's id and secret as the resource owner's username and password
Perform the request using the WebClientReactivePasswordTokenResponseClient
Set the access token as a bearer token for the request
Continue to the next function in the chain
Note that to use Spring Security's OAuth 2.0 Client features, you will need to configure your app also as a client. That means at least changing your DSL to include .oauth2Client() in addition to .oauth2ResourceServer(). It will also mean configuring a ClientRegistrationRepository. To keep my comment focused on filter functions, I've left that detail out, but I'd be happy to help there, too, if necessary.
How can I get details from the OAuth2 SSO Principal into my JWT? (instance of OAuth2Authentication getDetails as OAuth2AuthenticationDetails getDecodedDetails returns null)
I have...
Angular 6 client w/ implicit login as acme client (using angular-oauth2-oidc)
Spring Boot OAuth2 Authorization Server with JWT TokenService configuration w/ 3rd party SSO to GitHub
Auth server is configured with acme as implicit and GitHub client for SSO
Auth server exposes a /login/github
Auth server exposes a /me (protected by ResourceServer config)
When I login...
Angular app redirects to Auth service login
Auth service redirects to GitHub
[User Authenticates]
GitHub redirects to Auth Service
Auth Service initiates a session and issues a token
Auth Service redirects to Angular
The browser token is a proper JWT
Now, when I communicate with Auth Service /me:
Directly, I get a Principal that contains ALL of the details from GitHub (yay)
Indirectly from the Angular application passing the token via Authorization: Bearer ... header, I get a Principal that contains bare minimum OAuth client info for acme client (ugh)
I've tried a custom TokenEnhancer, but the OAuth2Authentication instance is already the bare minimum with no details. And, when the call is initiated from Angular, it doesn't have the same session cookie as when I call it directly (I don't want to share session - I want to put the details in the JWT).
[Update #1]
I tried a custom JwtAccessTokenConverter and used it in both of the #EnableAuthorizationServer and #EnableResourceServer (secures the /me endpoint) configuration classes. However it didn't work. I still get null details from OAuth2Authentication.
final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setAccessTokenConverter(new CustomTokenConverter());
The way Spring Lemon does this is replacing the OAuth2 and OpenID connect user services (see spring security docs). See LemonOAuth2UserService and LemonOidcUserService for details. For statelessness, it passes the client a shortlived JWT token as a param to targetUrl, as you can see in its OAuth2AuthenticationSuccessHandler class. It uses some cookies mechanism for doing all this statelessly, which can be further understood by looking at its HttpCookieOAuth2AuthorizationRequestRepository and how it's configured.
Here is an article explaining this in more details: https://www.naturalprogrammer.com/blog/1681261/spring-security-5-oauth2-login-signup-stateless-restful-web-services .
I'm using social network Vkontakte as Oauth2 authorization server. So I have several steps:
1) get code with request with request_type=code
2) get accessToken when I send request to access token uri
So I want to use Spring Oauth2, but I should get authorization code first, then access token, i've tried to add to application.yml :
authorized-grant-types: authorization_code
it's my application.yml:
security:
oauth2:
client:
clientId: [clientId]
clientSecret: [clientSecret]
accessTokenUri: https://oauth.vk.com/access_token
userAuthorizationUri: https://oauth.vk.com/authorize
tokenName: access_token
registered-redirect-uri: http://localhost:8080/login
resource:
token-info-uri: http://localhost:8080/user
but actually it doesn't help. If somebody faced it and know how to configure Spring Oauth2 app - will be grateful for help
Actually after couple days of investigation i figured out that Spring OAuth2 completely implementing all features and configuration to my client application uses the authorization code grant to obtain an access token from Vkontakte (the Authorization Server)
The only thing i need to do if i take as sample Spring Boot and OAuth2 social login simple is to populate application.yml with correct creds for my Authorization server:
security:
oauth2:
client:
clientId: xxxxxxx
clientSecret: xxxxxxxxxxx
accessTokenUri: https://oauth.vk.com/access_token
userAuthorizationUri: https://oauth.vk.com/authorize
tokenName: code
authenticationScheme: query
clientAuthenticationScheme: form
grant-type: authorization_code
resource:
userInfoUri: https://api.vk.com/method/users.get
The only problem i faced was providing correct token name and userInfoUri to retrieve logged user info.
According token name it is name of authorization code your get after passing authoriztion(response_type=token name, it calls code in my case) and use to get access token.
Hope it will be helpful people face the same problem