Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,77 @@ authState.performActionWithFreshTokens(service, new AuthStateAction() {
});
```

### Ending current session (Draft)

Given you have a logged in session and you want to end it. In that case you need to get:
- `AuthorizationServiceConfiguration`
- valid Open Id Token that you should get after authentication
- End of session URI that should be provided within you OpeId service config

First you have to build EndSessionRequest

```java
EndSessionRequest endSessionRequest = new EndSessionRequest(
authorizationServiceConfiguration,
idToken,
endSessionRedirectUri
);
```
This request can then be dispatched using one of two approaches.

a `startActivityForResult` call using an Intent returned from the `AuthorizationService`,
or by calling `performEndSessionRequest` and providing pending intent for completion
and cancelation handling activities.

The startActivityForResult approach is simpler to use but may require more processing of the result:

```java
private void endSession() {
AuthorizationService authService = new AuthorizationService(this);
Intent endSessionItent = authService.getAuthorizationRequestIntent(endSessionRequest);
startActivityForResult(endSessionItent, RC_END_SESSION);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RC_END_SESSION) {
EndSessionResonse resp = EndSessionResonse.fromIntent(data);
AuthorizationException ex = AuthorizationException.fromIntent(data);
// ... process the response or exception ...
} else {
// ...
}
}
```
If instead you wish to directly transition to another activity on completion or cancelation,
you can use `performEndSessionRequest`:

```java
AuthorizationService authService = new AuthorizationService(this);

authService.performEndSessionRequest(
endSessionRequest,
PendingIntent.getActivity(this, 0, new Intent(this, MyAuthCompleteActivity.class), 0),
PendingIntent.getActivity(this, 0, new Intent(this, MyAuthCanceledActivity.class), 0));
```

End session flow will also work involving browser mechanism that is described in authorization
mechanism session.
Handling response mechanism with transition to another activity should be as follows:

```java
public void onCreate(Bundle b) {
EndSessionResponse resp = EndSessionResponse.fromIntent(getIntent());
AuthorizationException ex = AuthorizationException.fromIntent(getIntent());
if (resp != null) {
// authorization completed
} else {
// authorization failed, check ex for more details
}
// ...
}
```

### AuthState persistence

Instances of `AuthState` keep track of the authorization and token
Expand Down
4 changes: 3 additions & 1 deletion app/README-Okta.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ You can create an Okta developer account at [https://developer.okta.com/](https:
| Setting | Value |
| ------------------- | --------------------------------------------------- |
| Application Name | OpenId Connect App *(must be unique)* |
| Redirect URIs | com.oktapreview.yoursubdomain://callback_url|
| Login redirect URIs | com.oktapreview.yoursubdomain://callback_url|
| Logout redirect URIs| com.oktapreview.yoursubdomain://callback_url|
| Allowed grant types | Authorization Code |

1. Click **Finish** to redirect back to the *General Settings* of your application.
Expand All @@ -27,6 +28,7 @@ You can create an Okta developer account at [https://developer.okta.com/](https:
{
"client_id": "{{YourClientID}}",
"redirect_uri": "com.oktapreview.{{yourOrg}}:/oauth",
"end_session_uri": "com.oktapreview.{{yourOrg}}:/{logoutCallback}",
"authorization_scope": "openid email profile",
"discovery_uri": "https://{{yourOrg}}.okta.com/.well-known/openid-configuration"
}
Expand Down
9 changes: 9 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ The configuration file MUST contain a JSON object. The following properties can
The value specified here should match the value specified for `appAuthRedirectScheme` in the
`build.gradle` (Module: app), so that the demo app can capture the response.

- `end_sesion_uri` (required): The redirect URI to use for receiving the end session response.
This should be a custom scheme URI (com.example.app:/oauth2redirect/example-provider).
Consult the documentation for your authorization server.

The value specified here should match the value specified for `appAuthRedirectScheme` in the
`build.gradle` (Module: app), so that the demo app can capture the response.

NOTE: Scheme of the URI should be the same as `redirect_uri` but callback should be different.

- `authorization_scope` (required): The scope string to use for the authorization request.
For the purposes of the demo, we recommend the value "openid profile email", though any value
understood by your authorization server can be used.
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ android {
// Make sure this is consistent with the redirect URI used in res/raw/auth_config.json,
// or specify additional redirect URIs in AndroidManifest.xml
manifestPlaceholders = [
'appAuthRedirectScheme': 'net.openid.appauthdemo'
'appAuthRedirectScheme': 'com.lohika.android.test'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be updated before merging to master

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it hasn't been merged yet?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because there is no maintainer - see #444.

]
}

Expand Down
14 changes: 14 additions & 0 deletions app/java/net/openid/appauthdemo/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,11 @@ public final class Configuration {
private String mClientId;
private String mScope;
private Uri mRedirectUri;
private Uri mEndSessionUri;
private Uri mDiscoveryUri;
private Uri mAuthEndpointUri;
private Uri mTokenEndpointUri;
private Uri mEndSessionEndpoint;
private Uri mRegistrationEndpointUri;
private Uri mUserInfoEndpointUri;
private boolean mHttpsRequired;
Expand Down Expand Up @@ -142,6 +144,11 @@ public Uri getDiscoveryUri() {
return mDiscoveryUri;
}

@Nullable
public Uri getmEndSessionUri() {
return mEndSessionUri;
}

@Nullable
public Uri getAuthEndpointUri() {
return mAuthEndpointUri;
Expand All @@ -152,6 +159,11 @@ public Uri getTokenEndpointUri() {
return mTokenEndpointUri;
}

@Nullable
public Uri getEndSessionEndpoint() {
return mEndSessionEndpoint;
}

@Nullable
public Uri getRegistrationEndpointUri() {
return mRegistrationEndpointUri;
Expand Down Expand Up @@ -196,6 +208,7 @@ private void readConfiguration() throws InvalidConfigurationException {
mClientId = getConfigString("client_id");
mScope = getRequiredConfigString("authorization_scope");
mRedirectUri = getRequiredConfigUri("redirect_uri");
mEndSessionUri = getRequiredConfigUri("end_session_uri");

if (!isRedirectUriRegistered()) {
throw new InvalidConfigurationException(
Expand All @@ -210,6 +223,7 @@ private void readConfiguration() throws InvalidConfigurationException {

mTokenEndpointUri = getRequiredConfigWebUri("token_endpoint_uri");
mUserInfoEndpointUri = getRequiredConfigWebUri("user_info_endpoint_uri");
mEndSessionEndpoint = getRequiredConfigUri("end_session_endpoint");

if (mClientId == null) {
mRegistrationEndpointUri = getRequiredConfigWebUri("registration_endpoint_uri");
Expand Down
1 change: 1 addition & 0 deletions app/java/net/openid/appauthdemo/LoginActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ private void initializeAppAuth() {
AuthorizationServiceConfiguration config = new AuthorizationServiceConfiguration(
mConfiguration.getAuthEndpointUri(),
mConfiguration.getTokenEndpointUri(),
mConfiguration.getEndSessionEndpoint(),
mConfiguration.getRegistrationEndpointUri());

mAuthStateManager.replace(new AuthState(config));
Expand Down
44 changes: 38 additions & 6 deletions app/java/net/openid/appauthdemo/TokenActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package net.openid.appauthdemo;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
Expand All @@ -36,6 +37,7 @@
import net.openid.appauth.AuthorizationService;
import net.openid.appauth.AuthorizationServiceDiscovery;
import net.openid.appauth.ClientAuthentication;
import net.openid.appauth.EndSessionRequest;
import net.openid.appauth.TokenRequest;
import net.openid.appauth.TokenResponse;

Expand Down Expand Up @@ -66,6 +68,8 @@ public class TokenActivity extends AppCompatActivity {

private static final String KEY_USER_INFO = "userInfo";

private static final int END_SESSION_REQUEST_CODE = 911;

private AuthorizationService mAuthService;
private AuthStateManager mStateManager;
private final AtomicReference<JSONObject> mUserInfoJson = new AtomicReference<>();
Expand Down Expand Up @@ -187,17 +191,17 @@ private void displayAuthorized() {

AuthState state = mStateManager.getCurrent();

TextView refreshTokenInfoView = (TextView) findViewById(R.id.refresh_token_info);
TextView refreshTokenInfoView = findViewById(R.id.refresh_token_info);
refreshTokenInfoView.setText((state.getRefreshToken() == null)
? R.string.no_refresh_token_returned
: R.string.refresh_token_returned);

TextView idTokenInfoView = (TextView) findViewById(R.id.id_token_info);
TextView idTokenInfoView = findViewById(R.id.id_token_info);
idTokenInfoView.setText((state.getIdToken()) == null
? R.string.no_id_token_returned
: R.string.id_token_returned);

TextView accessTokenInfoView = (TextView) findViewById(R.id.access_token_info);
TextView accessTokenInfoView = findViewById(R.id.access_token_info);
if (state.getAccessToken() == null) {
accessTokenInfoView.setText(R.string.no_access_token_returned);
} else {
Expand All @@ -213,13 +217,13 @@ private void displayAuthorized() {
}
}

Button refreshTokenButton = (Button) findViewById(R.id.refresh_token);
Button refreshTokenButton = findViewById(R.id.refresh_token);
refreshTokenButton.setVisibility(state.getRefreshToken() != null
? View.VISIBLE
: View.GONE);
refreshTokenButton.setOnClickListener((View view) -> refreshAccessToken());

Button viewProfileButton = (Button) findViewById(R.id.view_profile);
Button viewProfileButton = findViewById(R.id.view_profile);

AuthorizationServiceDiscovery discoveryDoc =
state.getAuthorizationServiceConfiguration().discoveryDoc;
Expand All @@ -231,7 +235,7 @@ private void displayAuthorized() {
viewProfileButton.setOnClickListener((View view) -> fetchUserInfo());
}

((Button)findViewById(R.id.sign_out)).setOnClickListener((View view) -> signOut());
findViewById(R.id.sign_out).setOnClickListener((View view) -> endOfSession());

View userInfoCard = findViewById(R.id.userinfo_card);
JSONObject userInfo = mUserInfoJson.get();
Expand Down Expand Up @@ -381,6 +385,24 @@ private void fetchUserInfo(String accessToken, String idToken, AuthorizationExce
});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == END_SESSION_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
signOut();
finish();
} else {
displayEndSessionCancelled();
}
}

private void displayEndSessionCancelled() {
Snackbar.make(findViewById(R.id.coordinator),
"Sign out canceled",
Snackbar.LENGTH_SHORT)
.show();
}

@MainThread
private void showSnackbar(String message) {
Snackbar.make(findViewById(R.id.coordinator),
Expand All @@ -389,6 +411,16 @@ private void showSnackbar(String message) {
.show();
}

@MainThread
private void endOfSession() {
Intent endSessionEnten = mAuthService.getEndSessionRequestIntent(
new EndSessionRequest(
mStateManager.getCurrent().getAuthorizationServiceConfiguration(),
mStateManager.getCurrent().getIdToken(),
mConfiguration.getmEndSessionUri()));
startActivityForResult(endSessionEnten, END_SESSION_REQUEST_CODE);
}

@MainThread
private void signOut() {
// discard the authorization and token state, but retain the configuration and
Expand Down
13 changes: 7 additions & 6 deletions app/res/raw/auth_config.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"client_id": "",
"redirect_uri": "net.openid.appauthdemo:/oauth2redirect",
"client_id": "0oahnzhsegzYjqETc0h7",
"redirect_uri": "com.lohika.android.test:/callback",
"end_session_uri":"com.lohika.android.test:/logout",
"authorization_scope": "openid email profile",
"discovery_uri": "",
"authorization_endpoint_uri": "",
"token_endpoint_uri": "",
"registration_endpoint_uri": "",
"discovery_uri": "https://lohika-um.oktapreview.com/oauth2/default/.well-known/openid-configuration",
"authorization_endpoint_uri": "https://lohika-um.oktapreview.com/oauth2/default/v1/authorize",
"token_endpoint_uri": "https://lohika-um.oktapreview.com/oauth2/default/v1/token",
"registration_endpoint_uri": "https://lohika-um.oktapreview.com/oauth2/v1/clients",
"user_info_endpoint_uri": "",
"https_required": true
}
Loading