In Part 1 and Part 2 of this series, we were introduced to Unified AppLinks, saw how to consume AppLinks as a plugin, and saw how to extend AppLinks to create our own custom application types and entity types to integrate with. In this final blog post we will see what’s involved in implementing a custom authentication provider, and how to add additional configuration screens for AppLinks.

State of Play

—————-
At the time of writing, Unified AppLinks supports three authentication types; HTTP Basic, OAuth 1.0, and Trusted Apps. HTTP Basic is very commonly supported, but has the limitation of only being able to authenticate as one user. OAuth is useful when your Atlassian application and the remote application do not have matching user bases. This protocol is tending towards being the new web standard in authentication between services. Atlassian has plans in future to also provide support for OAuth 2. Trusted Apps is a proprietary authentication protocol written by Atlassian for authenticated communication between Atlassian applications with the same user base. It is similar to “2 Legged OAuth“. Some other standard ways of authenticating with other applications are mentioned in this feature request for AppLinks.

Custom Authenticators

—————————-
Even with Atlassian implementing all the standard authentication types, there still may be times where an application needs a non standard authentication type implement, or, you may be impatient waiting for Atlassian to implement the latest standard in authentication. In our case today, authenticating with Twitter, we want to provide users with a simpler screen for configuring OAuth with Twitter, providing them with instructions and links to creating an application on Twitter, and also not requiring fields that are always the same for connecting to Twitter.

In contrast to to the previous blog posts in this series, I will not show much code here, but will refer to classes in the example code that I have provided on Bitbucket. Much of the code in there is very specific to a Twitter OAuth implementation, and so is not useful to show in this tutorial, which is intended to be for generic authentication services.

Authentication Provider Plugin Module

————————————————
A custom authentication provider has to implement at very least two things, an AuthenticationProviderPluginModule, and an AuthenticationProvider. The AuthenticationProvider class both serves as the class that provides authentication, but also serves as part of the API for plugin consumers. If plugin consumers want to use a particular type of authentication when they make a request, they may pass that AuthenticationProvider class into the `ApplicationLink.createAuthenticatedRequestFactory()`. For this reason, rather than exposing a class that should not be used directly by plugins, it is better to extend AuthenticationProvider with your own interface that you can expose, and then implement that interface with your implementation class.

There are two choices of authentication providers that you can implement (you can also implement both). The first is ImpersonatingAuthenticationProvider, which allows you to make requests on behalf of a user. OAuth is an example of an impersonating authentication provider. The other is NonImpersonatingAuthenticationProvider,which is for authenticators that just use static credentials, such as a configured username and password. For Twitter, we are using OAuth, so I’m going to implement the first one.

So my interface looks like this:

1
2
public interface TwitterOAuthAuthenticationProvider
extends ImpersonatingAuthenticationProvider {}

Next I need to implement the plugin module:

1
2
public class TwitterOAuthAuthenticationProviderPluginModule
implements AuthenticationProviderPluginModule {

This has three methods for me to implement. The simplest is `getAuthenticationProviderClass()`. This should return the interface that I created above:

1
2
3
public Class getAuthenticationProviderClass() {
return TwitterOAuthAuthenticationProvider.class;
}

The second is `getConfigUrl()`. This is used to return the URL for the screen that is used to configure this authentication. This screen will be displayed in an iframe on the AppLinks configuration dialog. If you want it to be cross product, the simplest way to implement this screen is using a servlet. For an example, see the TwitterOAuthAuthenticaitonProviderServlet implementation in the provided example code. This URL could also be a URL served by a remote system, which may be useful for for configuring a remote application to trust the local application. A key service for use with the servlet is AuthenticationConfigurationManager, which your plugin can use to store authentication configuration. My implementation to get the URL is as follows:

1
2
3
4
5
6
public String getConfigUrl(ApplicationLink link, Version applicationLinksVersion,
AuthenticationDirection direction, HttpServletRequest request) {
return TwitterOAuthUtil.getBaseURLFromRequest(request,
hostApplication.getBaseUrl()).toString() +
"/plugins/servlet/applinks/twitter/oauth/" + link.getId().get();
}

The final method to implement is `getAuthenticationProvider()`. This should return an instance of your authentication provider, if and only if the user has configured your authentication provider. For example:

1
2
3
4
5
6
7
8
9
public AuthenticationProvider getAuthenticationProvider(ApplicationLink link) {
if (authenticationConfigurationManager.isConfigured(link.getId(),
TwitterOAuthAuthenticationProvider.class)) {
return new TwitterOAuthAuthenticationProviderImpl(link,
TwitterOAuthUtil.getServiceProvider(), consumerService, consumerTokenStore,
requestFactory, hostApplication);
}
return null;
}

The AuthenticationProvider will implement one method (depending on which type of authentication provider you implement) which should return a request factory. Note that I’ve removed the constructor and dependencies, as they are all OAuth specific:

1
2
3
4
5
6
7
8
public class TwitterOAuthAuthenticationProviderImpl implements
TwitterOAuthAuthenticationProvider {
...
public ApplicationLinkRequestFactory getRequestFactory(String username) {
return new TwitterOAuthRequestFactory(applicationLink, serviceProvider,
consumerService, consumerTokenStore, requestFactory, hostApplication, username);
}
}

Implementing the Request Factory

———————
The key part of an authentication plugin module is implementing the request factory, and the request objects it returns. The interface to implement is[ApplicationLinkRequestFactory, and it has three methods to implement. The first one is `createRequest()`, the other two are specifically for use with ImpersonatingAuthenticationProvider’s, if `createRequest()` throws a CredentialsRequiredException. These methods should return a URI that consumers can either redirect or link to that a user can use to provide their credentials. This may be in the form of providing a username/password, or in our case, it will be a link to do the OAuth dance.

The simplest way to implement the `createRequest()` method is to delegate to the SAL RequestFactory to create the request, and wrap the returned request in your own request object to add the necessary credentials when execute is called.

1
2
3
4
5
6
7
8
public ApplicationLinkRequest createRequest(Request.MethodType methodType, String uri)
throws CredentialsRequiredException {
Request request = requestFactory.createRequest(methodType, uri);
ConsumerToken consumerToken = retrieveConsumerToken(username);
return new TwitterOAuthRequest(uri, methodType, request, serviceProvider,
consumerService, consumerToken, consumerTokenStore,
applicationLink.getId(), username);
}

For a complete picture on how this is implemented, checkout the source code.
Once you’ve implemented all this code, you can declare your custom authentication provider in `atlassian-plugin.xml` like so:

 

Additional Configuration Screens

—————————————–
The final plugin point that AppLinks provides is the ability to specify extra configuration for a particular AppLink. This is provided through the Atlassian plugin systems web-item plugin module. A web-item can be used to link to a screen that you provide to configure extra attributes for the AppLink. One of the great things about this mechanism is that multiple plugins that consume the AppLink can all provide their own extra config screens if need be.

The web-item should be declared to be in the `applinks.application.link.list.operation` section, and this link will be rendered next to each AppLink. Usually you will only want your web item displayed beside AppLinks of your custom application type, to do that, you can define a Condition that checks the AppLink type by getting the `applicationLink` key from the passed in context of the `shouldDisplay()` method. Although I haven’t included a web-item in my example plugin, here is an example from the JIRA FishEye plugin:

/secure/admin/EditFishEyeConfig!default.jspa?applicationLinkId=${applicationLink.id}

Conclusion

————–
We now have a complete AppLinks plugin implemented with a custom authentication provider. Extending AppLinks to add custom application types and entity types is very simple; with only a few pieces of code and no configuration screens, we have provided a way for users to configure links to non Atlassian applications and services. We have also seen how we can implement a custom authenticator, though this is a much more involved task.