### Description

`TransportGrantAction` currently provides an `executeWithGrantAuthentication` method which sub-classes can leverage. Instead of requiring sub-classes to invoke this method directly, we instead can move it into the `doExecute` method of `TransportGrantAction`, and allow inheritors to specify their own handler for grant authentication, something along the lines of: 

```
class TransportGrantAction {

    final void doExecute(...) {
        // executeWithGrantAuthentication can furthermore be inlined as part of this refactor
        executeWithGrantAuthentication(request, ActionListener.wrap(
            authentication -> { doSomethingWithAuthentication(...) }
        ));
    }

    abstract doSomethingWithAuthentication(...);
}
```

This reduces boilerplate and establishes a clearer contract for sub-classes of `TransportGrantAction`.
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/apikey/TransportGrantApiKeyAction.java
GrantApiKeyRequest::new,
            authenticationService,
            authorizationService,
            threadContext
        );
        this.apiKeyService = apiKeyService;
        this.resolver = resolver;
    }
    @Override
    protected void doExecute(Task task, GrantApiKeyRequest request, ActionListener<CreateApiKeyResponse> listener) {
        executeWithGrantAuthentication(
            request,
            listener.delegateFailure(
                (l, authentication) -> resolver.resolveUserRoleDescriptors(
                    authentication,
                    ActionListener.wrap(
                        roleDescriptors -> apiKeyService.createApiKey(
                            authentication,
                            request.getApiKeyRequest(),
                            roleDescriptors,
                            listener
                        ),
                        listener::onFailure
                    )
                )
            )
        );
    }
}