The OAuth protocol enables websites or applications (Consumers) to access Protected Resources from a web service (Service Provider) via an API, without requiring the User to disclose their Service Provider credentials to the Consumer. An example use case is allowing printing service print.example.com (the Consumer), to access private photos stored on photos.example.com (the Service Provider) without requiring the User to provide their photos.example.com credentials to print.example.com.
More generally, OAuth creates a freely-implementable and generic methodology for API authentication, benefitting developers who want their Consumer product to interact with various Service Providers.
While OAuth does not require a specific user interface or interaction pattern, recommendations and emerging best practices are described in this specification. Additionally, OAuth does not specify how Service Providers authenticate Users, making the protocol ideally suited for cases where authentication credentials are unavailable to the Consumer, such as with OpenID.
OAuth aims to unify the experience and implementation of delegated web service authentication into a single, community-driven protocol. OAuth builds on existing protocols and best practices that have been independently implemented by various websites. An open standard, supported by large and small providers alike, promotes a consistent and trusted experience for both application developers and the users of those applications.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC2119.
Consumer are categorized based on their ability to:
OAuth defines three Consumer types:
OAuth includes a Consumer Key and matching Consumer Secret that together authenticate the Consumer (as opposed to the User) to the Service Provider. Customer-specific identification allows the Service Provider to vary access levels to Consumers (such as un-throttled API access or other resources). Only web-based Consumers are assumed to be capable of keeping their Consumer Secret private.
Service Providers MUST enable Consumer Developers to obtain a Consumer Key and Consumer Secret. The process and requirements for provisioning these are entirely up to the Service Providers unless where otherwise specified.
The Consumer Secret MAY be either a randomized string or a public/private key pair. In the case that the Consumer Secret is a public/private key pair, the Service Provider only requires the public key, and SHOULD verify that the Consumer Developer is, in fact, the owner of the private key.
The Service Provider MUST also provide documentation to specify:
oauth_
.The Consumer Developer MUST establish a Consumer Key and a Consumer Secret with the Service Provider.
When registering a Consumer Key, the Consumer Developer MUST identify the Consumer type (web-based, desktop, or manual token entry). If the Consumer is web-based, the Consumer Developer MUST provide the Callback Endpoint URL where the Service Provider will send Tokens.
Request parameter names and values are escaped using the RFC 3986 percent-encoding (%xx) mechanism. Characters not in the unreserved character set (RFC 3986 section 2.3) MUST be encoded. Characters in the unreserved character set MUST NOT be encoded. Hexadecimal characters in encodings MUST be upper case.
OAuth Authentication is done in two broad steps. First, the Consumer MUST obtain a Single-Use Token from the Service Provider by having the the User successfully sign into the Service Provider and grant access. Second, exchange the Single-Use Token for a Multi-Use Token which can be used to make API calls to Service Provider.
There are three variations to the first step of obtaining an authorized Single-Use Token. The variations are based on the Consumer type. The second step of exchanging a Single-Use Token for a Multi-Use Token is the same for all Consumer types. The following sections describe the process for obtaining Single-Use Tokens (for each Consumer type), and obtaining a Multi-Use Token. Each of these subsections is accompanied by a diagram showing the complete authentication process for the Consumer type.
OAuth does not specify how the Service Provider authenticates the User. However, it does provide a set of steps to verify User identity and obtain approval to grant the Consumer access to the Protected Resources.
Web-based Consumers obtain a Single-Use Token by way of an HTTP Redirect from the Consumer to the Service Provider (Authorization Endpoint URL) and back to the Consumer (Callback Endpoint URL), following these steps:
To request a Single-Use Token and Token Secret, the Consumer constructs an HTTP GET request to the Service Provider's Authorization Endpoint URL, to be sent via the User's web browser.
The request contains the following parameters:
oauth_consumer_key
: The Consumer Key.oauth_state
: An OPTIONAL parameter that will be passed through unmodified to the Consumer (this parameter SHOULD only be used in the case of stateless web-based Consumers).Once the request URL has been constructed the Consumer issues an HTTP 302 Redirect to the User's web-browser using that URL.
For example, if the Consumer is requesting a Single-Use Token from the Authorization Endpoint URL https://sp.example.com/oauth/authorize
with the Consumer Key 0685bd91
the request would be:
https://sp.example.com/oauth/authorize?oauth_consumer_key=0685bd91
The Service Provider verifies the User's identity and asks for consent as detailed in the "User Authentication and Consumer Access Grants" section.
After the User has authenticated with the Service Provider and has granted permission for Consumer access, the Service Provider will create a Single-Use Token and a corresponding Token Secret, and deliver it to the Consumer via an HTTP Redirect.
The response for a web-based Consumer includes the following GET parameters:
oauth_token
: The Single-Use Token.oauth_token_secret
: The Token Secret.oauth_state
: REQUIRED if one was provided in the Consumer request.For example, if the Service Provider has created a Single-Use Token c3e347b6a5001c16
and Token Secret 678dfge7dghek243
for the Consumer, whose Callback Endpoint URL is https://Consumer.example.com/validate
, then the Service Provider will redirect the User to the following URL:
https://Consumer.example.com/validate?oauth_token=c3e347b6a5001c16&oauth_token_secret=678dfge7dghek243
Desktop Consumers MUST first obtain a Single-Use Token, since they cannot provide a Callback Endpoint URL to which the Service Provider redirects the User.
To obtain a Single-Use Token, the Consumer sends a request to the Service Provider's API Endpoint URL. The request contains the following parameters:
oauth_consumer_key
: The Consumer Key.oauth_nonce
: A one-time use value to verify the request has not been made before. See the "Request Nonce" section. oauth_ts
: An integer representing the time of request, expressed in number of seconds after January 1, 1970 00:00:00 GMT.oauth_sigalg
: The hashing algorithm that the Consumer used to sign the request. oauth_sig
: The signature. For example, the Consumer has a Consumer Key 1b20acc6
and a Consumer Secret 427a5979d9df7722
. The Service Provider's API Endpoint URL for requesting a Single-Use Token is http://sp.example.com/oauth/get_su_token
, and the request is using the HTTP GET method. The nonce is<PHONE_NUMBER>4999140772853922434221488511
and the timestamp of<PHONE_NUMBER>
. Using sha1
as the signing algorithm, the signature is then 26287279e66f7f183af02653e823625871167d16
, and the request is as follows:
https://sp.example.com/oauth/get_su_token?oauth_consumer_key=1b20acc6&oauth_nonce=17907867114999140772853922434221488511&oauth_ts=1186953553&oauth_sigalg=sha1&oauth_sig=26287279e66f7f183af02653e823625871167d16
The Service Provider generates a Single-Use Token and MUST ensure it cannot be exchanged for a Multi-Use Token until the User successfully grants access in step 5 below.
For example, the Service Provider generates a Single-Use Token 37bb49b4
and a corresponding Token Secret d0e46c19
, and returns the following as the body of the response:
token=37bb49b4
secret=d0e46c19
In order for the Consumer to be able to exchange the pre-obtained Single-Use Token for a Multi-Use Token, the Consumer MUST obtain approval from the User by directing the User to the Service Provider. The Consumer constructs an HTTP GET request to the Service Provider's Authorization Endpoint URL which will be sent via the User's web browser.
This request contains the following parameters:
oauth_consumer_key
: The Consumer Key oauth_token
: The Single-Use Token obtained in the previous step.Once the request URL has been generated the Consumer directs the User to the URL.
For example, the Consumer requests the User to authorise a Single-Use Token at the Service Provider's Authorization Endpoint URL https://sp.example.com/oauth/authorize
with the Consumer Key 0685bd91
and pre-obtained Single-Use Token 37bb49b4
.
https://sp.example.com/oauth/authorize?oauth_consumer_key=0685bd91&oauth_token=37bb49b4
The Service Provider verifies the User's identity and asks for consent as detailed in the "User Authentication and Consumer Access Grants" section.
The Service Provider instructs the User to inform the Consumer that authorization has completed.
Manual-token-entry Consumers cannot redirect the User to the Service Provider nor accept incoming HTTP(S) requests from the Service Provider. Instead, A Single-Use Token and Token Secret will be generated by the Service Provider and presented to the User. The Consumer waits for the User to manually submit the Token and Token Secret back to it.
Note: If a Service Provider knows a Consumer to be running on a mobile device or set-top box, the Service Provider SHOULD ensure that the Authorization Endpoint URL, the Single-Use Token, and Token Secret are short and simple for manual entry.
The Consumer directs the User to the Service Provider's Authorization Endpoint URL.
For example, the Consumer is requesting a Single-Use Token and Token Secret from the Service Provider's Authorization Endpoint URL https://sp.example.com/oauth/authorize
with the Consumer Key 0685bd91
.
https://sp.example.com/oauth/authorize?oauth_consumer_key=0685bd91
The Service Provider verifies the User's identity and asks for consent as detailed in the "User Authentication and Consumer Access Grants" section.
The Service Provider present the Single-Use Token and Token Secret to the User by displaying it on the screen or other available methods.
The User manually enters the Single-Use Token and Token Secret in the Consumer. This can be done by manually typing the information or using cut-and-paste.
Obtaining a Multi-Use Token follows the same process for all Consumer types.
The Single-Use Token and Token Secret will be exchanged for a Multi-Use Token and Token Secret.
To request a Multi-Use Token, the Consumer makes an HTTP request to an API Endpoint URL for Token exchange as specified by the Service Provider's documentation. The Service Provider documentation MUST specifies the HTTP verb (GET or POST) for this endpoint. It is RECOMMENDED that this be a POST request. The request MUST be signed with the Single-Use Token Secret per "Signing API Requests".
The request contains the following parameters:
oauth_consumer_key
: The Consumer Key.oauth_token
: The Single-Use Token obtained previously.oauth_nonce
: A one-time use value to verify the request has not been made before. See the "Request Nonce" section. oauth_ts
: An integer representing the time of request, expressed in number of seconds after January 1, 1970 00:00:00 GMT.oauth_sigalg
: The hashing algorithm that the Consumer used to sign the request. oauth_sig
: The signature. The Service Provider exchanges the Consumer's Single-Use Token, with new Multi-Use Token and Token Secret. The Service Provider MUST verify that:
On successful verification, the Service Provider returns a Multi-Use Token and a Token Secret in the body of the response as newline separated name-value pairs, followed by any additional data as defined by the Service Provider's documentation.
For example:
token=37bb49b4
secret=d0e46c19
The Multi-Use Token and Token Secret are stored by the Consumer to use when signing future API requests.
If the request is unsuccessful, the Service Provider MUST respond with a HTTP 401 Not Authorized, and MAY include some further details about why the request wasn't authorized.
After receiving the Multi-Use Token and Token Secret, the Consumer is able to use the Multi-Use Token and Token Secret to access the Protected Resources on behalf of the User. The Service Provider documentation MAY specify limitations on the Multi-Use Token such as expiration or other constrains.
The request contains the following parameters:
oauth_consumer_key
: The Consumer Key.oauth_token
: The Multi-Use Token.oauth_nonce
: A one-time use value to verify the request has not been made before. See the "Request Nonce" section. oauth_ts
: An integer representing the time of request, expressed in number of seconds after January 1, 1970 00:00:00 GMT.oauth_sigalg
: The hashing algorithm that the Consumer used to sign the request. oauth_sig
: The signature. All API Endpoint URL requests MUST be signed by the Consumer and the signature verified by the Service Provider. Both the Consumer and Service Provider follow the same process to generate the request signature. The Consumer generates a signature and includes it with the request (using the 'oauth_sig' parameter) while the Service Provider generates a signature and compares it to the signature provided by the Consumer. The signature process includes the following steps:
The signature process MUST NOT change the request parameter names or values. It is a read-only process on the request. All request parameters MUST have be properly encoded as described in "HTTP Request Parameters" prior to applying the signature process.
All Service Provider request parameters (parameters starting with something other than oauth_
) SHALL be normalized as follows into a single string:
a=1
, c=hi%20there
, f=25
, f=50
, z=10
.&
character (ASCII code 38). For example: a=1&c=hi%20there&f=25&f=50&z=10
The OAuth request parameters, custom request parameters, and other request properties are concatenated into a single string used as input to the signing algorithm. The request parameters MUST be concatenated in the following order:
oauth_secret
: The Consumer Secret.oauth_consumer_key
: The Consumer Key.oauth_token
: The Single-Use or Multi-Use Token.oauth_token_secret
: The Token Secret.http_request_method
: The HTTP request method used to send the request. Value is case-sensitive and MUST be one of: HEAD
,GET
, POST
, PUT
, DELETE
, TRACE
, OPTIONS
, and CONNECT
.http_request_uri
: The API Endpoint URL and Service Provider specific parameters excluding any OAuth parameters (those starting with 'oauth_').normalized_request_parameters
: The string result from step 1.oauth_nonce
: The request nonce.oauth_ts
: An integer representing the time of request, expressed in number of seconds after January 1, 1970 00:00:00 GMT.The above request parameter are concatenated in order into a single string. For each parameter, the name is separated from the corresponding value by an = character (ASCII code 61). Each name-value pair is seperated by an &
character (ASCII code 38).
The concatenated string from step 3 (concatenated_string
) is signed or hashed according to the chosen signature algorithm (indicated with the oauth_sigalg
parameter):
oauth_sig=md5
: md5(concatenated_string)oauth_sig=sha1
: sha1(concatenated_string)oauth_sig=hmac_sha256
: hmac_sha256(oauth_secret, concatenated_string)oauth_sig=openssl_x509_sign
: openssl_x509_sign(private_certificate, concatenated_string). In the case of using x509 certs, the Service Provider would have the Consumer's public key and therefore would provide an empty string as the secret.123456
The basic API request looks like so:
http://twitter.com/statuses/with_friends/123456.json?page=3&count=50
Assuming the use of the sha1
signature method and the following values:
oauth_secret
=> 3a2cd35
oauth_consumer_key
=> 0685bd91
oauth_token
=> 540ad18
oauth_token_secret
=> x2s55k0
oauth_nonce
=> MTgzNTYxODk4Mw
oauth_ts
=><PHONE_NUMBER>
oauth_sig = sha1("oauth_secret=3a2cd35&oauth_consumer_key=0685bd91&oauth_token=540ad18&oauth_token_secret=x2s55k0&http_request_method=GET&http_request_uri=http%3A%2F%2Ftwitter.com%2Fstatuses%2Ffriends/123456.json&normalized_request_parameters=count%3D50%26page%3D3&oauth_nonce=MTgzNTYxODk4Mw&oauth_ts=1185517832")
We need to pass to the server (in cleartext) the Consumer Key (oauth_consumer_key
), the Token (oauth_token
), the timestamp (oauth_ts
), the nonce (oauth_nonce
), the signing algorithm (oauth_sigalg), and the signed request (oauth_sig
).
Consumers MUST NOT send any of the secrets with the request, since doing so would compromise the entire request, as well as subsequent requests.
The full signed request is then:
GET http://twitter.com/statuses/friends/123456.json?page=3&count=50&oauth_key=0685bd91&oauth_token=540ad18&oauth_nonce=MTgzNTYxODk4Mw&oauth_ts=1181537927&oauth_sigalg=sha1&oauth_sig=2d047740b53ae16f670750bef5bb5c2a0f398f30
A nonce is a random string, uniquely generated for each request. The nonce allows the Service Provider to verify that a request has never been made before and helps prevent against replay attacks. More information about nonces can be found on Wikipedia (http://en.wikipedia.org/wiki/Cryptographic_nonce).
This section defines an RFC 2617 extension to support OAuth. It uses the standard HTTP Authorization and WWW-Authenticate headers to pass credentials rather than using URL parameters. All Consumers SHOULD be able to supply an OAuth Authorization header as an alternative to passing URL parameters. It is RECOMMENDED that Service Providers accept such a header, and such Service Providers MUST signal Consumers by returning the OAuth WWW-Authenticate header upon all requests for the Protected Resources. For the remainder of this section, we assume that the Service Provider under discussion has chosen to support this extension.
When using a HTTP-Authorization header, the OAuth request parameters are not included in the request URL. Rather they are included as the value of the header.
`Authorization: OAuth oauth_token="540ad18" oauth_key="0685bd91" oauth_nonce="MTgzNTYxODk4Mw" oauth_ts="1185517832" oauth_sigalg="SHA1" oauth_sig="2d047740b53ae16f670750bef5bb5c2a0f398f30"`
To reject a request that lacks appropriate credentials, the Service Provider MUST respond with a 401 Unauthorized response. Such a response MUST include at least one OAuth WWW-Authenticate header and MAY include additional WWW-Authenticate headers:
`401 Unauthorized`
...
`WWW-Authenticate: OAuth realm="https://sp.example.com/oauth/authorize?res=74637288"`
The realm parameter both defines a protection realm per RFC 2617, section 1.2, and also specifies the Authorization Endpoint URL to be used by the Consumer to obtain authorization for the accessed resource.
A Consumer MAY also include an empty, token-less OAuth Authorization header on any HTTP request to inform a Service Provider that it supports OAuth. The Service Provider MUST then respond with the appropriate WWW-Authenticate header for the requested resource in the response. For example, a Consumer could inquire using HEAD and then adjust its UI if the Service Provider supports OAuth.


Web Services are a very common way to implement a Service Oriented Architecture (SOA).
There are lots of web service standards/specifications (XML, SOAP, WSDL, UUDI, WS-*, ...) coming from organizations like W3C, OASIS, WS-I, ... And there are java web service standards like JAX-WS 1.x (JSR 181), JAX-WS 2.0 (JSR 224).
OpenEJB provides a standard way to implement web services transport protocol throughout the JAX-WS specification. Java basic standards for web services (JAX-WS) do lack some features that are required in most real world applications, e.g. standard ways for handling security and authentication (there is no java specification for Oasis's WS-Security specification).
OpenEJB provides two mechanisms to secure webservices - HTTP authentication and WS-Security:
HTTPS : works at the transport level, enables a point-to-point security. It has no impact on developments. It allows you :
Warning: Currently only BASIC is the only HTTP authentication mechanism available when running OpenEJB standalone or in a unit test, but we hope to support DIGEST in the future.
WS-Security: works at the message (SOAP) level, enables a higher-level security, Nowadays, SOAP implementations use other protocols than just HTTP so we need to apply security to the message itself and not only at the transport layer. Moreover, HTTPS can only be used for securing point-to-point services which tend to decrease with Enterprise Service Bus for example.
The Oasis organization has defined a standard (part of well-known WS-*) which aims at providing high level features in the context of web services: WS-Security. It provides a standard way to secure your services above and beyond transport level protocols such as HTTPS. WS-Security relies on other standards like XML-Encryption.
Main features are:
Again, JAX-WS doesn't standardize security for web services. OpenEJB provides a common and highly configurable way to configure WS-Security in association with the JAX-WS usage without vendor dependence. Internally, OpenEJB integrates Apache WSS4J as the WS-Security implementation. To use the integration, you will need to configure WSS4J using the openejb-jar.xml.
Warning: the proposed WS-Security integration is only used at server side. Currently, WS-Security client configuration is not managed by OpenEJB. You can use the JAX-WS API to create a stub and then rely on the implementation to set up WS-Security properties.
This configuration file lets you set up incoming and outgoing security parameters. Incoming and outgoing configuration is independent so that you can configure either one or the other or both. You can decide to check client credentials for incoming messages and sign outgoing messages (response).
The configuration is made in the openejb-jar.xml. Each endpoint web
service can provide a set of properties to customize WS-Security behavior
through the
<properties>
wss4j.in.action = UsernameToken
wss4j.in.passwordType = PasswordDigest
wss4j.in.passwordCallbackClass=org.superbiz.calculator.CustomPasswordHandler
</properties>
In order to recover WSS4J properties both for input and output, we use
naming conventions.
Each property is made of
For example : wss4j.in.action = UsernameToken
<openejb-jar xmlns="http://tomee.apache.org/xml/ns/openejb-jar-2.2">
<enterprise-beans>
...
<session>
<ejb-name>CalculatorImpl</ejb-name>
<web-service-security>
<security-realm-name/>
<transport-guarantee>NONE</transport-guarantee>
<auth-method>WS-SECURITY</auth-method>
<properties>
wss4j.in.action = UsernameToken
wss4j.in.passwordType = PasswordDigest
wss4j.in.passwordCallbackClass=org.superbiz.calculator.CustomPasswordHandler
</properties>
</web-service-security>
</session>
...
</enterprise-beans>
</openejb-jar>
This request contains SOAP headers to manage security. You can see UsernameToken tag from the WS-Security specification.
POST /CalculatorImplUsernameTokenHashedPassword HTTP/1.1
Content-Type: text/xml; charset=UTF-8
SOAPAction: ""
Accept: *
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.5.0_05
Host: <IP_ADDRESS>:8204
Connection: keep-alive
Transfer-Encoding: chunked
524
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soap:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
wsu:Id="UsernameToken-22402238"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:Username xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">jane</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">tf7k3a4GREIt1xec/KXVmBdRNIg=</wsse:Password>
<wsse:Nonce xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">cKhUhmjQ1hGYPsdOLez5kA==</wsse:Nonce>
<wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2009-04-14T20:16:26.203Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<ns1:sum xmlns:ns1="http://superbiz.org/wsdl">
<arg0>4</arg0>
<arg1>6</arg1>
</ns1:sum>
</soap:Body>
</soap:Envelope>
HTTP/1.1 200 OK
Content-Length: 200
Connection: close
Content-Type: text/xml; charset=UTF-8
Server: OpenEJB/??? (unknown os)
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns1:sumResponse xmlns:ns1="http://superbiz.org/wsdl">
<return>10</return>
</ns1:sumResponse>
</soap:Body>
</soap:Envelope>
@RolesAllowed doesn't work straight off with WS-Security, but you can add calls to the OpenEJB SecurityService to login to a JAAS provider to a CallbackHandler. Once you have done this, any permissions configured with @RolesAllowed should be honoured.
Here is a snippet from the webservice-ws-security example demonstrating this:
public class CustomPasswordHandler implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN) {
// TODO get the password from the users.properties if possible
pc.setPassword("waterfall");
} else if (pc.getUsage() == WSPasswordCallback.DECRYPT) {
pc.setPassword("serverPassword");
} else if (pc.getUsage() == WSPasswordCallback.SIGNATURE) {
pc.setPassword("serverPassword");
}
if ((pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN) || (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN)) {
SecurityService securityService = SystemInstance.get().getComponent(SecurityService.class);
Object token = null;
try {
securityService.disassociate();
token = securityService.login(pc.getIdentifer(), pc.getPassword());
securityService.associate(token);
} catch (LoginException e) {
e.printStackTrace();
throw new SecurityException("wrong password");
}
}
}
}
A full example (webservice-ws-security) is available with OpenEJB Examples.