Skip to content
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

Support generation of authorization code via an API #15

Open
bluesliverx opened this issue Jul 3, 2019 · 0 comments
Open

Support generation of authorization code via an API #15

bluesliverx opened this issue Jul 3, 2019 · 0 comments

Comments

@bluesliverx
Copy link
Collaborator

(Migrated from bluesliverx/grails-spring-security-oauth2-provider#89 reported by @ghost, see that issue for more details)

Background

I'm using the plugin to implement an OAuth provider in an application that consists of two components

Frontend App

The frontend app is a single-page app (SPA) and consists of HTML/CSS/JS only. It retrieves it's data via REST calls to the backend Grails app.

Backend App

The backend Grails app has the OAuth provider plugin installed and provides data to the frontend app. There is no view code in this app and it is deployed on a separate host than the frontend app.

The Spring Security REST plugin is also installed, and a JSON Web Token is added to each API request to identify authenticated users.

Problem

The difficulty with using the plugin as-is, is that it presumes the OAuth views will be rendered on the server-side by GSPs. In my case, this is contrary to how the app has been designed to work, specifically, the login, create account, forgot password, etc. views already exist in the frontend app, and we'd obviously like to re-use these for the OAuth views, rather than reimplement them as GSPs.

Proposal

When a client requests an access token, a better workflow for us is:

  1. If the user is not authenticated, they must first of all authenticate via our existing views
  2. Once the user has authenticated, show an OAuth approval dialog, which is implemented as a view in the frontend app (where all the other view code exists)
  3. If approval is granted, make an API request to the backend to generate an authorization code
  4. When the frontend app receives the authorization code it is forwarded to the client, which can exchange it for an access token with the backend app directly.

Solution

The only change the plugin requires to support this proposal is the ability to generate an authorization code programatically. I've implemented the following service and it appears to work

import grails.plugin.springsecurity.SpringSecurityService
import org.springframework.security.oauth2.provider.AuthorizationRequest
import org.springframework.security.oauth2.provider.OAuth2Authentication
import org.springframework.security.oauth2.provider.OAuth2Request
import org.springframework.security.oauth2.provider.OAuth2RequestFactory
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices

class OAuthProviderService {

    OAuth2RequestFactory oauth2RequestFactory
    SpringSecurityService springSecurityService
    AuthorizationCodeServices authorizationCodeServices

    String generateAuthorizationCode(AuthorizationCodeRequest command) {

        // the command object should already have been validated (e.g in the controller)
        // by the time we get to this point   

        Map requestParams = [
                scope: command.scope,
                response_type: command.responseType,
                redirect_uri: command.redirectUri,
                client_id: command.clientId
        ]

        // create an object representing an approved request for an authorization code, i.e. after the user has
        // granted their approval via the consent dialog
        AuthorizationRequest approvedAuthCodeRequest = oauth2RequestFactory.createAuthorizationRequest(requestParams)
        approvedAuthCodeRequest.approved = true

        OAuth2Request storedOAuth2Request = oauth2RequestFactory.createOAuth2Request(approvedAuthCodeRequest)
        OAuth2Authentication combinedAuth = new OAuth2Authentication(storedOAuth2Request, springSecurityService.authentication)
        authorizationCodeServices.createAuthorizationCode(combinedAuth)
    }
}
import grails.validation.Validateable
import org.example.Client

@Validateable
class AuthorizationCodeRequest {
    String scope
    String responseType
    String redirectUri
    String clientId

    // these constraints perform much of the same validation logic as 
    // org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint
    // which would otherwise be omitted
    static constraints = {

        def getClient = {
            Client.findByClientId(it, [cache: true])
        }

        clientId validator: {
            getClient(it) != null
        }

        responseType inList: ["token", "code"]

        redirectUri validator: { uri, AuthorizationCodeRequest self ->

            Client client = getClient(self.clientId)

            if (client) {
                uri in client.redirectUris
            }
        }

        scope validator: { scope, AuthorizationCodeRequest self ->
            Client client = getClient(self.clientId)

            if (client) {
                scope in client.scopes
            }
        }
    }
}

Obviously this is not a complete solution, e.g. an endpoint also needs to be added that calls this service. Is this something that you'd consider adding to the plugin?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant