summaryrefslogtreecommitdiffstats
path: root/remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java
blob: a18c2f463782a5a430a090b226c7908d05be9d07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chromoting;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.view.KeyEvent;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;

import org.chromium.chromoting.jni.JniInterface;

/**
 * This class performs the user-interaction needed to authenticate the session connection. This
 * includes showing the PIN prompt and requesting tokens for third-party authentication.
 */
public class SessionAuthenticator {
    /**
     * Application context used for getting user preferences, displaying UI, and fetching
     * third-party tokens.
     */
    private Chromoting mApplicationContext;

    /** Provides the tokenUrlPatterns for this host during fetchThirdPartyTokens(). */
    private HostInfo mHost;

    /** Object for fetching OAuth2 access tokens from third party authorization servers. */
    private ThirdPartyTokenFetcher mTokenFetcher;

    public SessionAuthenticator(Chromoting context, HostInfo host) {
        mApplicationContext = context;
        mHost = host;
    }

    public void displayAuthenticationPrompt(boolean pairingSupported) {
        AlertDialog.Builder pinPrompt = new AlertDialog.Builder(mApplicationContext);
        pinPrompt.setTitle(mApplicationContext.getString(R.string.title_authenticate));
        pinPrompt.setMessage(mApplicationContext.getString(R.string.pin_message_android));
        pinPrompt.setIcon(android.R.drawable.ic_lock_lock);

        final View pinEntry =
                mApplicationContext.getLayoutInflater().inflate(R.layout.pin_dialog, null);
        pinPrompt.setView(pinEntry);

        final TextView pinTextView = (TextView) pinEntry.findViewById(R.id.pin_dialog_text);
        final CheckBox pinCheckBox = (CheckBox) pinEntry.findViewById(R.id.pin_dialog_check);

        if (!pairingSupported) {
            pinCheckBox.setChecked(false);
            pinCheckBox.setVisibility(View.GONE);
        }

        pinPrompt.setPositiveButton(
                R.string.connect_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (JniInterface.isConnected()) {
                            JniInterface.handleAuthenticationResponse(
                                    String.valueOf(pinTextView.getText()),
                                    pinCheckBox.isChecked(), Build.MODEL);
                        } else {
                            String message =
                                    mApplicationContext.getString(R.string.error_network_error);
                            Toast.makeText(mApplicationContext, message, Toast.LENGTH_LONG).show();
                        }
                    }
                });

        pinPrompt.setNegativeButton(
                R.string.cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        JniInterface.disconnectFromHost();
                    }
                });

        final AlertDialog pinDialog = pinPrompt.create();

        pinTextView.setOnEditorActionListener(
                new TextView.OnEditorActionListener() {
                    @Override
                    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                        // The user pressed enter on the keypad (equivalent to the connect button).
                        pinDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick();
                        pinDialog.dismiss();
                        return true;
                    }
                });

        pinDialog.setOnCancelListener(
                new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        // The user backed out of the dialog (equivalent to the cancel button).
                        pinDialog.getButton(AlertDialog.BUTTON_NEGATIVE).performClick();
                    }
                });

        pinDialog.show();
    }

    /** Saves newly-received pairing credentials to permanent storage. */
    public void commitPairingCredentials(String host, String id, String secret) {
        // Empty |id| indicates that pairing needs to be removed.
        if (id.isEmpty()) {
            mApplicationContext.getPreferences(Activity.MODE_PRIVATE).edit()
                    .remove(host + "_id")
                    .remove(host + "_secret")
                    .apply();
        } else {
            mApplicationContext.getPreferences(Activity.MODE_PRIVATE).edit()
                    .putString(host + "_id", id)
                    .putString(host + "_secret", secret)
                    .apply();
        }
    }

    public void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) {
        assert mTokenFetcher == null;

        ThirdPartyTokenFetcher.Callback callback = new ThirdPartyTokenFetcher.Callback() {
            @Override
            public void onTokenFetched(String code, String accessToken) {
                // The native client sends the OAuth authorization code to the host as the token so
                // that the host can obtain the shared secret from the third party authorization
                // server.
                String token = code;

                // The native client uses the OAuth access token as the shared secret to
                // authenticate itself with the host using spake.
                String sharedSecret = accessToken;

                JniInterface.onThirdPartyTokenFetched(token, sharedSecret);
            }
        };
        mTokenFetcher = new ThirdPartyTokenFetcher(mApplicationContext, mHost.getTokenUrlPatterns(),
                callback);
        mTokenFetcher.fetchToken(tokenUrl, clientId, scope);
    }

    public void onNewIntent(Intent intent) {
        if (mTokenFetcher != null) {
            if (mTokenFetcher.handleTokenFetched(intent)) {
                mTokenFetcher = null;
            }
        }
    }

    /** Returns the pairing ID for the given host, or an empty string if not set. */
    public String getPairingId(String hostId) {
        SharedPreferences prefs = mApplicationContext.getPreferences(Activity.MODE_PRIVATE);
        return prefs.getString(hostId + "_id", "");
    }

    /** Returns the pairing secret for the given host, or an empty string if not set. */
    public String getPairingSecret(String hostId) {
        SharedPreferences prefs = mApplicationContext.getPreferences(Activity.MODE_PRIVATE);
        return prefs.getString(hostId + "_secret", "");
    }
}